1

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

2
  • it will be better to add your models to the current question, more readable for other people Commented Sep 21, 2017 at 9:58
  • alright just a sec Commented Sep 21, 2017 at 9:59

2 Answers 2

1

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]
Sign up to request clarification or add additional context in comments.

10 Comments

how Category with "added": false will be in your query? what is your logic? OP get it by added_f = categories.extra(select={'added': '0'}).exclude(outfits__pk=obj.pk)
@devxplorer Can you explain it more?
@BearBrown It will LEFT join outfits with specified id, then grouped by category and count outfits items for category, if category have no outfits it will get COUNT(outfit_id) > 0 as false. But maybe I did not fully understand what the author wants
@JohnBaek please see comment above, and tell please did you tried this, did it return correct results?
@devxplorer i test your solution, it's wrong, repo for test
|
0

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.

2 Comments

the code in the question works correctly, but currently OP want to know is there a way to minimize query to database.
I see, and for that the answer of using raw SQL might be correct (I was certainly not trying to bash that answer!). But I think the code in question might not be how you'd best set up your API architecture in terms of performance, so I suggested to reconsider the output instead of trying to create it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.