Django and reverse relations from foreign keys - django

suppose the following simple models:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
author = models.ForeignKey(User, related_name='author_set')
How can I get all the authors that participated in a particular blog (i.e. pk=1)? I tried something like this, but it didn't work.
User.objects.author_set.filter(blog=Blog.objects.get(pk=1))
Many thanks in advance!

User.objects.filter(author_set__blog__pk=1)
I wasn't paying attention to your related name, so the code above (revised) now uses the proper related name. However, this is a really bad related name. The related name should describe what's on the opposite side, i.e. Entry. So really it should be related_name='entry_set'. However, that's the default anyways, so you can remove related_name if you just want that. I would typically use a simple pluralized version of the related class, which would be in this case related_name='entries'. Or, you might want to use something like "blog_entries" to be more specific.

Related

Get list of values from grandchildren records

I'm trying to access the grandchildren records in a list to avoid duplicate records. In this example, a tag can only be used once across articles for a given author. I will use the resulting list of grandchildren records in my clean function to return validation errors.
class Article(models.Model):
tag = models.ManyToManyField(Tag)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Tag(models.Model):
class Author(models.Model):
Right now I can do this:
print(author.articles.first().tag.first())
Travel
I'd like to be able to use something like author.articles.tags.all() to return the list and check the submitted form against it to raise a ValidationError message to the user.
How can this be done efficiently with the basic Many-to-Many setup without creating an intermediate table for the tag relationships? This is solely in the Admin interface, in case that matters at all.
i come from the link you posted on upwork,
the way i understand your question,
what you want to achieve seems to be impossible ,
what i think can work , is to fetch articles related to the author, with their corresponding tags,
after that they are retrieved you do filtering and remove duplicates.
otherwise the tag has nothing to connect it with the author,,
I'm so jealous that's a great answer #since9teen94
Be aware, I will not base my answer in the easiest solution but how we model reality (or how we should do it).
We put tags after things exists right?
In that order I will make your life horrible with something like this:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=30, null=False)
class Article(models.Model):
name = models.CharField(max_length=30, null=False)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Tag(models.Model):
name = models.CharField(max_length=30, null=False, unique=True)
articles = models.ManyToManyField(Article)
but believe me you don't want to follow this approach it will make your work harder.
You can search for Articles based on Tags directly
Tag.objects.filter(name='Scify').first().articles.all() # name is unique
The real issue with this is that the reverse lookup is really complex (I mean.. get ready for debug)
Article.objects.filter(
id__in=list(
Tag.objects.filter(name='Scify').first().articles.values_list('id', flat=True)
)
)
I am sure this does not solve your problem and I don't like complex code for no reason but if you're open to suggestions I don't mind to think different and add some options
Edit:
About the author and clean repeated tags.. well you don't have to deal with that and if you want to find all Tag your author has you could loop the tags
for tag in Tag.objects.all():
if tag.articles.filter(author__name='StackoverflowContributor'):
print(tag.name)
# > Scify
I am just saying that there are options not that this is the best for you but don't be afraid of the terminal, it's really cool
The Django ORM is pretty cool when you get used to it, but regular SQL is pretty cool too
# created_at, updated_at, name and tag_val are variables I
# added due to my slight ocd lol
class Author(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Tag(models.Model):
tag_val = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Article(models.Model):
author = models.ForeignKey(Author,related_name='articles', on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, related_name='articles')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
I can write my query like this, assuming the variable 'author' has been assigned an Author object instance, and get a list of dictionaries [{'tags':1},{'tags':2}] where the value is the auto generated primary key id of the Tag object instance
author.articles.values('tags').distinct()

What is different between django foreigin key in string and without string?

I am not getting why people write foreign key in two way and what is the purpose of this? are they both same or any different?
I notice some people write like:
author = models.ForeignKey(Author, on_delete=models.CASCADE)
and some people write it like:
author = models.ForeignKey('Author', on_delete=models.CASCADE)
What is different between these? is there any special purpose of writing like this or they both are same?
What is different between these? is there any special purpose of writing like this or they both are same?
They both result in the same link yes. The string will later be "resolved", and eventually the ForeignKey will point to the Author model.
Using strings however is sometimes the only way to make references however, if the models to which you target need to be defined yet. For example in the case of a cyclic reference.
Imagine for example that you define relations like:
class Author(models.Model):
name = models.CharField(max_length=128)
favorite_book = models.ForeignKey(Book, null=True, on_delete=models.SET_NULL)
class Book(models.Model):
title = models.CharField(max_length=128)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Here a Book refers to an Author, and an Author refers to a Book. But since the Book class is not constructed at the time you construct the ForeignKey, this will give a NameError.
We can not define the Author after we defined the Book, since then we refer to the Author before it is constructed (and this thus will again yield a NameError).
We can however use strings here, th avoid the circular reference, like:
class Author(models.Model):
name = models.CharField(max_length=128)
favorite_book = models.ForeignKey('Book', null=True, on_delete=models.SET_NULL)
class Book(models.Model):
title = models.CharField(max_length=128)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
By using a string, it is fine for the Python interpreter, since you do not use an identifier that is not yet defined, and Django will then, when the models are loaded, replace the strings with a reference to the corresponding model.
The documentation on a ForeignKey [Django-doc]:
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself (...)
If the model is defined in another app, then you can refer to it with app_name.ModelName.
Say you have laid your models out like this:
models/
__init__.py
model_a.py
model_b.py
This is a common layout when you have an app with a lot of models and you want to better organize your code. Now say ModelA has a foreign key to ModelB and ModelB has a foreign key to ModelA. You cannot have both files importing the other model because you would have a circular import.
Referencing another model by string allows you to "lazily" reference another model that it has not yet loaded, this solves the problem of having circular imports

Django Symmetric Many-to-Many In Both Tables

One thing I found frustrating in Django is the seemingly required asymmetry when defining many-to-many relationships. I teach Django and would really like to find the "most elegant" way to describe and teach many-to-many relationships in Django.
One of my students used the technique of putting the class name in as a string in making her many-to-many model. This allows her to avoid less-than-intuitive techniques like related-name. This is my simplified version of her model - the key is that Person and Course are strings, not class names.
class Person(models.Model):
email = models.CharField(max_length=128, unique=True)
courses = models.ManyToManyField('Course', through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
class Course(models.Model):
title = models.CharField(max_length=128, unique=True)
members = models.ManyToManyField('Person', through='Membership')
But while this looks pretty to me, I am concerned that I have messed things up. I have done some basic testing and see no downsides to this style of model definition, but I am concerned that I messed something up that I don't even understand. So I submit this as a question, "What is wrong with this picture?"
There's nothing unusual or controversial about using strings to define a relationship field; this is fully documented. But it doesn't have anything to do with making the relationship symmetrical.
I'm not clear why your student has defined the relationship twice. That seems unnecessary, as does the use of an explicit through table. Your definition is exactly equivalent to this much simpler one:
class Person(models.Model):
email = models.CharField(max_length=128, unique=True)
courses = models.ManyToManyField('Course', related_name='members')
class Course(models.Model):
title = models.CharField(max_length=128, unique=True)
Note there is no need to define anything on Course, and no need for an explicit through table - which, even if it has no extra fields, disables some functionality like inline admin forms. Given a Course object, you can do my_course.members.all() to get the members just as in your version.

Django - How to build an intermediate m2m model that fits? / best practice

First of all I have to admit that I'm quite new to all this coding stuff but as I couldn't find a proper solution doing it myself and learning to code is probably the best way.
Anyway, I'm trying to build an app to show different titleholders, championships and stuff like that. After reading the Django documentation I figured out I have to use intermediate models as a better way. My old models.py looks like this:
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
[...]
class Team(models.Model):
name = models.CharField(max_length=64)
team_member_one = models.ForeignKey(Person)
team_member_two = models.ForeignKey(Person)
class Championship(models.Model):
name = models.CharField(max_length=128)
status = models.BooleanField(default=True)
class Titleholder(models.Model):
championship = models.ForeignKey(Championship)
date_won = models.DateField(null=True,blank=True)
date_lost = models.DateField(null=True,blank=True)
titleholder_one = models.ForeignKey(Person,related_name='titleholder_one',null=True,blank=True)
titleholder_two = models.ForeignKey(Person,related_name='titleholder_two',null=True,blank=True)
Championships can be won by either individuals or teams, depending if it's a singles or team championship, that's why I had to foreign keys in the Titleholder class. Looking at the Django documentation this just seems false. On the other hand, for me as a beginner, the intermediate model in the example just doesn't seem to fit my model in any way.
Long story short: can anyone point me in the right direction on how to build the model the right way? Note: this is merely a question on how to build the models and displaying it in the Django admin, I don't even talk about building the templates as of now.
Help would be greatly appreciated! Thanks in advance guys.
So I will take it up from scratch. You seem to be somewhat new to E-R Database Modelling. If I was trying to do what you do, I would create my models the following way.
Firstly, Team would've been my "corner" model (I use this term to mean models that do not have any FK fields), and then Person model would come into play as follows.
class Team(models.Model):
name = models.CharField(max_length=64)
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
team = models.ForeignKey(to=Team, null=True, blank=True, related_name='members')
This effectively makes the models scalable, and even if you are never going to have more than two people in a team, this is good practice.
Next comes the Championship model. I would connect this model directly with the Person model as a many-to-many relationship with a 'through' model as follows.
class Championship(models.Model):
name = models.CharField(max_length=64)
status = models.BooleanField(default=False) # This is not a great name for a field. I think should be more meaningful.
winners = models.ManyToManyField(to=Person, related_name='championships', through='Title')
class Title(models.Model):
championship = models.ForeignKey(to=Championship, related_name='titles')
winner = models.ForeignKey(to=Person, related_name='titles')
date = models.DateField(null=True, blank=True)
This is just the way I would've done it, based on what I understood. I am sure I did not understand everything that you're trying to do. As my understanding changes, I might modify these models to suit my need.
Another approach that can be taken is by using a GenericForeignKey field to create a field that could be a FK to either the Team model or the Person model. Or another thing that can be changed could be you adding another model to hold details of each time a championship has been held. There are many ways to go about it, and no one correct way.
Let me know if you have any questions, or anything I haven't dealt with. I will try and modify the answer as per the need.

Defining a two way many to many in Django

Just starting in Python/Django framework so sorry if this is dumb... but i cant find any solution.
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField(Contact)
class Contact(models.Model):
name = models.CharField(max_length=200)
dealers = models.ManyToManyField(Dealer)
I have this relation set up however when I run SyncDB it doesnt work. It tells me that Contact is not defined on this line
contacts = models.ManyToManyField(Contact)
Im more familiar with compiled languages. Is there anyway to tell python that the contact class exists, or better yet is there a special syntax im missing for defining this kind of relation.
I dont see a need for a two way ManyToMany in both the models, as they are a M:N relationship (2 way relationship).
Your issue here is, Contact is not defined at the point of execution of this code:
contacts = models.ManyToManyField(Contact)
So You need to wrap it in quotes for it to work
contacts = models.ManyToManyField('Contact')
Documentation on that can be found here
I would recommend the following models:
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField('Contact')
class Contact(models.Model):
name = models.CharField(max_length=200)
and It would do exactly what you are looking for.
You can read about ManyToMany relationships here. The same link also covers how to handle Reverse m2m queries
If you want to do a two way ManyToMany both, you just only need to do this:
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField('Contact', blank=True)
class Contact(models.Model):
name = models.CharField(max_length=200)
dealers = models.ManyToManyField('Dealer', through=Dealer.projects.through, blank=True)
I guess it will work to you.