merry-go-round
merry-go-round

Reputation: 4615

Can you optimize this code? (Django, python)

I'm adding 'added' field to check which categories User's Post(Outfit) is added to. It sounds horrible, so let's dive in to the code.

I want to optimize get_categories(self, obj) function.

class CategorySerializer(serializers.ModelSerializer):
    added = serializers.BooleanField()
    class Meta:
        model = Category
        fields = (
            'id',
            'name',
            'added'
        )


class OutfitDetailSerializer(serializers.ModelSerializer):

    def get_categories(self, obj):
        user = self.context['request'].user
        categories = Category.objects.filter(owner=user)
        added = categories.extra(select={'added': '1'}).filter(outfits__pk=obj.pk)
        added = list(added.values('added', 'name', 'id'))
        added_f = categories.extra(select={'added': '0'}).exclude(outfits__pk=obj.pk)
        added_f = list(added_f.values('added', 'name', 'id'))
        categories = added + added_f
        return CategorySerializer(categories, many=True).data

The output is below!

"categories": [{
        "id": 1,
        "name": "Gym",
        "added": true
    }, {
        "id": 2,
        "name": "School",
        "added": false
    }, {
        "id": 3,
        "name": "hollymo",
        "added": true
    }, {
        "id": 4,
        "name": "Normal",
        "added": false
    }, {
        "id": 6,
        "name": "New Category",
        "added": false
    }
]

Here is models.py

class Outfit(models.Model):
    ...
    user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
    content = models.CharField(max_length=30)
    ...

class Category(models.Model):
    name = models.CharField(max_length=20)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
    outfits = models.ManyToManyField(Outfit, related_name="categories", blank=True)
    main_img = models.ImageField(
                            upload_to=upload_location_category,
                            null=True,
                            blank=True)
    ...

here the repo for test

Upvotes: 1

Views: 133

Answers (2)

Vincent
Vincent

Reputation: 324

If I understand your use case correctly you just want "to check which categories User's Post(Outfit) is added to". For that you would only need to return the ones with added = true right? and then you could leave the added key out.

as in:

"categories": [{
        "id": 1,
        "name": "Gym"
    }, {
        "id": 3,
        "name": "hollymo"
    }
]

If so, you could just use:

import Category from category.models

class CategoriesSerializer(serializers.ModelSerializer):

    class Meta:
        model = Category
        fields = ('id', 'name')

class OutfitDetailSerializer(serializers.ModelSerializer):
    categories = CategoriesSerializer(many=True)

If instead your use case is to show a list of all categories and then do something with just the ones that the current outfit is added to, I'd suggest doing 2 API calls instead of your current logic; One with the answer I supplied above and one to get all categories. Then do that 'added' logic in your front-end as its presentation layer logic imo.

I'd certainly try to avoid doing raw SQL queries in Django, it cuts the purpose of migrations and is rarely necessary.

Upvotes: 0

devxplorer
devxplorer

Reputation: 637

If i get you right, you can get necessary data with django raw sql:

q = """\
SELECT yourappname_category.id,
       yourappname_category.name,
       COUNT(outfit_id) > 0 as added 
FROM yourappname_category
  LEFT JOIN yourappname_category_outfits 
      ON yourappname_category.id = yourappname_category_outfits.category_id 
         AND yourappname_category_outfits.outfit_id=%s
WHERE yourappname_category.owner_id=%s
GROUP BY yourappname_category.id, yourappname_category.name"""

categories = Category.objects.raw(q, [obj.id, user.id])
results = [{'id': c.id, 'name': c.name, 'added': c.added} for c in categories]

Upvotes: 1

Related Questions