Create object in model having foreigkey relation - django

I want to create an entry in this Something model in python manage.py shell using this
Someting.objects.create(discussion_title="General", user_username="admin", content="Hello")
models example
class Discussion(models.Model):
title = models.CharField(max_length=255, unique=True, blank=False,)
users = models.ManyToManyField(User, blank=True, )
class Something(models.Model):
user = models.ForeignKey(User,
on_delete=models.CASCADE)
discussion = models.ForeignKey(Discussion, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
content = models.TextField(unique=False, blank=False)
I am getting this error
TypeError: Something() got an unexpected keyword argument 'discussion_title'

First, you have to use double under bar __ to use django's model relation expression.
Someting.objects.get(discussion__title="General", user__username="admin", content="Hello")
Second, you can't use double under bar relation expression when create an object.
if you want to create an object in relation, you have to create in step by step. follow #Nicolas Appriou 's answer

Your Something model does not have a discussion_title field. You need to create a Discussion instance for this.
This model does not have a user_username model either.
discussion = Discussion.objects.create(title="Foobar")
discussion.users.add(User.objects.create(username="Ham")
Something.objects.create(
discussion=discussion,
)

Related

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:
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:
I wrote the _get_all_active_surveys() function and there I get all the surveys that are with a survey_status = 'active' and then the _get_all_followups() function needs to call it to use the result to build its own one. I have an issue telling me that
a list is not a callable object.
I tried to write directly the right query in _get_all_followups() with
queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')
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
i read that a related_name can help to build reverse query but i don't understand why?
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
Quite a few things to answer here, I've put them into a list:
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.
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)
You can comma-separate filter expressions as .filter(expression1, expression2) rather than .filter(expression1).filter(expression2).
You are missing an underscore in surveystatus_survey_status it should be surveystatus__survey_status.
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()

Django: Annotate with field from another table (one-to-many)

Good day.
I wish to annotate my model with information from a different table.
class CompetitionTeam(models.Model):
competition_id = models.ForeignKey('Competition', on_delete=models.CASCADE, to_field='id', db_column='competition_id')
team_id = models.ForeignKey('Team', on_delete=models.CASCADE, to_field='id', null=True, db_column='team_id')
...
class Team(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
teamleader_id = models.ForeignKey('User', on_delete=models.CASCADE, to_field='id', db_column='teamleader_id')
...
class Competition(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
...
Looping through my competitions, I wish to retrieve the list of competitionteam objects to be displayed with the relevant team's name. I tried:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=...)
-where instead of the ellipses I put Subquery expressions in. However, I'm unsure of how to match the team_id variable. eg.
*.anotate(name=Subquery(Team.objects.filter(id=competitionteam.team_id)).values('name'))
Related is the question: Django annotate field value from another model but I am unsure of how to implement that in this case. In that case, in place of mymodel_id, I used team_id but it only had parameters from the Team object, not my competition team object. I didn't really understand OuterRef but here is my attempt that failed:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=Subquery(Team.objects.get(id=OuterRef('team_id'))))
"Error: This queryset contains a reference to an outer query and may only be used in a subquery."
The solution for my question was:
CompetitionTeam.objects.filter(
competition_id=_competition.id,
team_id__in=joined_team_ids
).annotate(
name=Subquery(
Team.objects.filter(
id=OuterRef('team_id')
).values('name')
))
Thanks.

Django constraint between ManyToManyField and ForeignKey

Consider I have following models:
class Cargo(models.Model):
name = models.CharField(default='')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class Box(models.Model):
name = models.CharField(default='')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
tags = models.ManyToManyField(Cargo, blank=True)
I want to avoid situation when I add some cargo object to box with different owner. For example:
cargo = Cargo(owner=1)
box = Box(owner=2)
box.add(cargo)
How to add such a constraint on a model level?
My initial thought is that a great solution to this problem would be to define a custom RelatedManager that overrides the add() method and validates that the user is the same before you actually do the link. However, after searching through the internet for a while, I was unable to find a way to do a custom RelatedManager on the ManyToManyField (docs on RelatedManager).
As a workaround, I would recommend that you create a method on the Box model called addCargo which you use to add cargo. The method could then enforce the validation of the users before adding the cargo. It could look like:
class Box(models.Model):
name = models.CharField(default='')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
tags = models.ManyToManyField(Cargo, blank=True)
def addCargo(self, cargo):
if self.owner.id != cargo.owner.id:
raise ValueError("cargo and box must have same user")
self.tags.add(cargo)
And your code to add the cargo would look like:
cargo = Cargo(owner=1)
box = Box(owner=2)
box.addCargo(cargo)
Hope this helps!

could django update foreignkey use SQL?

I filled datas into postgreSQL without type foreignkey at first.
here is my models.py
class BeverageMenu(models.Model):
brand = models.CharField(max_length=255, null=True)
area = models.CharField(max_length=50, blank=True, null=True)
class DMenu(models.Model):
dmenu = models.ForeignKey(BeverageMenu,null=True,blank=True)
category = models.CharField(max_length=255, null=True)
product = models.CharField(max_length=255, null=True)
and I use this way to update the foreignkey:
>>> from psql.models import BeverageMenu,DMenu
>>> menu1 = BeverageMenu.objects.get(id=1)
>>>product = DMenu.objects.filter(area='North')
>>>product.update(dmenu=menu1)
And I want to know could I use SQL directly to do this ?
I try this but fail
INSERT INTO psql_dmenu(category,product,dmenu) VALUES ('hot','soup',1),
ERROR: column "dmenu" of relation "psql_dmenu" does not exist
You could, but why would you want to? Django has a model layer for a reason, which is to make the database easier to deal with and less dependent on SQL.
However, for your problem, the issue is that the underlying database column for a ForeignKey includes the prefix _id: so your field is dmenu_id.

Sort by related field in Django ORM

I have a Notes and a NoteRefs fields where the NoteRefs has a foreign key to the Notes. I need to query the Notes but order by the related field (ie. the NoteRefs' start_ref field).
How might I do that through the django ORM? Here's kinda what works in SQL
SELECT
note.user_id,
note.content,
note.created,
note.modified
FROM noteref
INNER JOIN note
ON note.id = noteref.note_id
ORDER BY noteref.start_ref
I can't use Note.order_by('related_field'), because the related field isn't part of the Note Model. From what I can tell, that seems to be what the documentation says to do. How can I sort on the related field here?
EDIT: Model information
class Note(models.Model):
user = models.ForeignKey(User, db_index=True)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class NoteRef(models.Model):
note = models.ForeignKey(Note, db_index=True)
_order = models.IntegerField(default=0)
book = models.IntegerField(max_length=2)
start_ref = models.IntegerField(max_length=8, db_index=True)
end_ref = models.IntegerField(max_length=8, db_index=True)
ref_range = models.IntegerField()
passage = models.CharField(max_length=50)
You should try Note.objects.order_by("noterefs__start_ref")
The documentation doesn't make this very clear, as it uses a ForeignKey to self, but it works.
Now, the docs also warn against the possibility of duplicate objects showing up if you have multiple NoteRefs for a single Note, so you should double-check this.