Mike
Mike

Reputation: 3

how to build query with several manyTomany relationships - Django

I really don't understand all the ways to build the right query.

I have the following models in the code i'm working on. I can't change models.

models/FollowUp:

class FollowUp(BaseModel):
    name = models.CharField(max_length=256)
    questions = models.ManyToManyField(Question, blank=True, )

models/Survey:

class Survey(BaseModel): name = models.CharField(max_length=256)

followup = models.ManyToManyField(
    FollowUp, blank=True, help_text='questionnaires')

user = models.ManyToManyField(User, blank=True, through='SurveyStatus')

models/SurveyStatus:

class SurveyStatus(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
    survey_status = models.CharField(max_length=10,
                                     blank=True,
                                     null=True,
                                     choices=STATUS_SURVEY_CHOICES,
                                     )

models/UserSurvey:

class UserSurvey(BaseModel):
    user = models.ForeignKey(User, null=True, blank=True,
                             on_delete=models.DO_NOTHING)
    followups = models.ManyToManyField(FollowUp, blank=True)
    surveys = models.ManyToManyField(Survey, blank=True)
    questions = models.ManyToManyField(Question, blank=True)

    @classmethod
    def create(cls, user_id):
        user = User.objects.filter(pk=user_id).first()
        cu_quest = cls(user=user)
        cu_quest.save()
        cu_quest._get_all_active_surveys
        cu_quest._get_all_followups()
        cu_quest._get_all_questions()
        return cu_quest

    def _get_all_questions(self):
        [[self.questions.add(ques) for ques in qstnr.questions.all()]
         for qstnr in self.followups.all()]
        return

    def _get_all_followups(self):
        queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')

        # queryset = self._get_all_active_surveys()

        [self.followups.add(quest) for quest in queryset]
        return

    @property
    def _get_all_active_surveys(self):
        queryset = Survey.objects.filter(user=self.user, 
                   surveystatus__survey_status='active')
        [self.surveys.add(quest) for quest in queryset]
        return 

Now my questions:

  1. my view sends to the create of the UserSurvey model in order to create a questionary. I need to get all the questions of the followup of the surveys with a survey_status = 'active' for the user (the one who clicks on a button)... I tried several things:

a list is not a callable object.

but I don't succeed to manage all the M2M relationships. I wrote the query above but issue also

Related Field got invalid lookup: surveystatus_survey_status

  1. i read that a related_name can help to build reverse query but i don't understand why?

  2. it's the first time i see return empty and what it needs to return above. Why this notation?

If you have clear explanations (more than the doc) I will very appreciate.

thanks

Upvotes: 0

Views: 94

Answers (1)

0sVoid
0sVoid

Reputation: 2663

Quite a few things to answer here, I've put them into a list:

  1. Your _get_all_active_surveys has the @property decorator but neither of the other two methods do? It isn't actually a property so I would remove it.

  2. You are using a list comprehension to add your queryset objects to the m2m field, this is unnecessary as you don't actually want a list object and can be rewritten as e.g. self.surveys.add(*queryset)

  3. You can comma-separate filter expressions as .filter(expression1, expression2) rather than .filter(expression1).filter(expression2).

  4. You are missing an underscore in surveystatus_survey_status it should be surveystatus__survey_status.

  5. Related name is just another way of reverse-accessing relationships, it doesn't actually change how the relationship exists - by default Django will do something like ModelA.modelb_set.all() - you can do reverse_name="my_model_bs" and then ModelA.my_model_bs.all()

Upvotes: 0

Related Questions