1

I'm designing an api using python DRF for a merchant profile page. ID should not be required to be passed into the api.

I'm trying to do a simple fetch to get one object to return as a result and I read that retrieve can be used for this function.

So here's my code:

models.py:

# Create your models here. class Merchant(models.Model): uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True) user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) def __str__(self): return self.merchantprofile.organization_name class MerchantProfile(models.Model): uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True) merchant = models.OneToOneField(Merchant, on_delete=models.CASCADE) organization_name = models.CharField(max_length=255) since = models.DateTimeField(auto_now_add=True) mobile_number = models.CharField(max_length=20, null=True, blank=True) email = models.EmailField(null=True, blank=True) website_url = models.CharField(max_length=100, null=True, blank=True) 

views.py:

class MerchantProfileViewSet(AuthenticationMixin, RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): queryset = MerchantProfile.objects.all() serializer_class = MerchantProfileSerializer lookup_field = "uuid" def retrieve(self, request, *args, **kwargs): user_id = self.request.user.id instance = MerchantProfile.objects.get(merchant__user=user_id) serializer = MerchantProfileSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) 

urls.py:

merchant_router = DefaultRouter() merchant_router.register('/profile', MerchantProfileViewSet, basename="merchantprofile") urlpatterns = [ path('merchant', include(merchant_router.urls)), ] 

serializer.py

class MerchantProfileSerializer(serializers.ModelSerializer): class Meta: model = MerchantProfile fields = ["organization_name", 'since', 'mobile_number', 'email', 'website_url', 'facebook_url', 'instagram_url', 'tiktok_url', 'youtube_url', 'xhs_url'] 

No matter how I tweaked, I always get this

enter image description here

but this is how I want it to look like ( I managed to get this by using list method, but I was told that I should be able to achieve with retrieve and retrieve should be the way to go instead of using list which is a workaround in this particular case)

enter image description here

 def list(self, request): user_id = self.request.user.id try: qs = MerchantProfile.objects.get(merchant__user=user_id) serializer = self.serializer_class(qs) return Response(serializer.data, status=status.HTTP_200_OK) except: raise Http404("Merchant profile does not exist for the current user") 

Would appreciate if someone can point out my mistakes and give me some guidance since I am very new to this, thank you!

1 Answer 1

0

There are a few ways to do this, but the one I came up with that I felt maximized the amount default DRF framework behavior I was leveraging was:

  1. Inherit the default RetrieveModelMixin and UpdateModelMixin into my ViewSet
  2. Override the get_object function to return the profile. Since this is helper function that both RetrieveModelMixin and UpdateModelMixin rely on you don't actually need to implement or override queryset.
  3. Map the GET /profile route to the retrieve method of my ViewSet. This is the key thing I think you are missing. The default is be for resouce_name/$ to be mapped to list method, but by just manually mapping it to retrieve you get the desired behavior you want.

Here's what my code looks like. (In my case I am returning the profile of the logged in user at the /profile route.)

serializers.py: (Totally normal, nothing special here)

class ProfileSerialzer(serializers.ModelSerializer): class Meta: model = Profile read_only_fields = ['user'] fields = '__all__' 

views.py: (Override get_object with logic that returns the correct single profile resource given the route)

class ProfileViewSet( mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet, ): serializer_class = ProfileSerialzer def get_object(self): return self.request.user.profile 

urls.py: (Rather than using router.register and the ViewSet default path-to-method mappings, manually set up the url pattern and map the get HTTP verb on your path to the retrieve method)

router.register(r'companies', CompanyViewSet, basename='company') # Example of a "normal" ViewSet urlpatterns = [ path('', include(router.urls)), path( 'profile/', ProfileViewSet.as_view({'get': 'retrieve', 'put': 'update'}), name='profile', ) ] urlpatterns += router.urls 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.