In the Django documentation about related_name it says the following:
The name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
I didn't understand it clearly. If somebody would please explain it a bit more, it would help me a lot.
When you create a foreign key, you are linking two models together. The model with the ForeignKey() field uses the field name to look up the other model. It also implicitly adds a member to the linked model referring back to this one.
class Post(models.Model):
# ... fields ...
class Comment(models.Model):
# ... fields ...
post = models.ForeignKey(Post, related_name=???)
There are three possible scenarios here:
1. Don't specify related_name
If you don't specify a name, django will create one by default for you.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all()
The default name is the relation's name + _set.
2. Specify a custom value
Usually you want to specify something to make it more natural. For example, related_name="comments".
some_post = Post.objects.get(id=12345)
comments = some_post.comments.all()
3. Prevent the reverse reference from being created
Sometimes you don't want to add the reference to the foreign model, so use related_name="+" to not create it.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all() # <-- error, no way to access directly
related_query_name is basically the same idea, but when using filter() on a queryset:
posts_by_user = Post.objects.filter(comments__user__id=123)
But to be honest I've never used this since the related_name value is used by default.
If in a model you have a ForeignKey field (this means you point through this field to other model):
class Author(models.Model):
name = ......
email = .....
class Article(models.Model):
author = models.ForeignKey(Author)
title= ....
body = ....
if you specify related_name on this field
class Article(modles.Model):
author = models.ForeignKey(Author, related_name='articles')
you give a name to the attribute that you can use for the relation (named reverse realationship) from the related object back to this one (from Author to Article). After defining this you can retrieve the articles of an user like so:
author.articles.all()
If you don't define a related_name attribute, Django will use the lowercase name of the model followed by _set (that is, in our case, article_set) to name the relationship from the related object back to this one, so you would have to retrieve all articles of an user like so:
author.article_set.all()
If you don't want to be possible a reverse relationship (from the model to which points your ForeignKey filed to this model (the model in which the ForeignKey field is defined) you can set
class Author(models.Model):
author = models.ForeignKey(User, related_name='+')
Related
I'm want to create nested categories, model work fine.
class Category(models.Model):
category_name = models.CharField(max_length=100)
children_ids = models.ManyToManyField(
"Category", blank=True, related_name="categories"
)
...etc
but, when i add inline for admin panel
class ChildrensInline(admin.TabularInline):
model = Category.children_ids.through
compiler shows me error:
'categories.Category_children_ids' has more than one ForeignKey to 'categories.Category'. You must specify a 'fk_name' attribute.
I also try fk_name='categories', and columns name inside Category_children_ids table, but it not work
The value for fk_name has to be the name of the class (lower-case), prefixed with either "from_" or "to_", depending on your needs. So in your case, it has to be either fk_name='from_category' or fk_name='to_category':
class ChildrensInline(admin.TabularInline):
model = Category.children_ids.through
fk_name='from_category' # or: 'to_category'
Generally, a quick way to figure such things out is to (transiently) place a simple print(model.__dict__) pretty much anywhere in your code, here e.g. right after the second line. Then all fields of model will be shown in the console output.
I am writing a Django Model and different Forms to modify the model's data.
For simplification let's say my model is as follows:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_date = models.DateField()
# (...) some other properties
__identity_card_front = models.FileField(blank=True)
class IdentityCardForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('__identity_card_front',)
My question is: must the fields names correspond to the attributes of Profile class (the exact name of the variables definition)? Do these strings determine the HTML form field name that will be searched in form validation? And if so, how can I customise it? I don't want to have to obligatorily call my field name '__identity_card_front', but instead maybe something like 'id_front', 'idf', etc.
I have seen labels might be used, but I was not clear how Django treats fields tags. I could not find either a good explanation on the Docs without getting lost around low-level class definitions and properties.
Note: I am using django-2.0
Here's a version of my models.py file. I've removed irrelevant fields and the model names are made up, for security reasons:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
Although it is not explicitly defined, there is a many-to-many relationship between an Actor and a FilmStudio. This is evidenced by the following call to the Python API:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson').distinct()
This returns all of the FilmStudio objects which Samuel L. Jackson is related to, and each one only once. What I'd like is to define extra fields on the relationship between an Actor and a FilmStudio (it doesn't work too well in this example, I know, but it makes sense for my scenario).
Following what is described in Extra fields on many-to-many relationships, I could use an intermediate model to define extra fields on the relationship between a Film and an Actor, for instance.
But this doesn't seem to help me with my problem. I don't want to define the Actor to FilmStudio relationship explicitly, since it's an existing relationship based on other relationships.
Is it possible to define fields on the relationship that I'm describing?
As far as I know, you are not able to do that.
The reason for that is that it is nowhere to store the extra fields of that relationship. If I understand you correctly, these "extra fields" are not implicit in the actor-film or productionstudio-film relationships, so even though you say they are implicit, the extra fields themselves are explicit.
You could try to emulate it by creating an explicit direct relationship whenever it is needed. Then you could simulate the extra fields using the model as an abstraction, but I am not sure if this is what you want. If you opt for this kind of solution you can use default values (in your abstraction) for filling in relationships that don't have a instance yet.
Does that explanation make sense to you?
EDIT:
(I have not double checked that the code works, so be vary)
OK, so you have the original models:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
# The "solution" would be:
class ActorProductionComapny(models.Model):
production_company = models.ForeignKey(ProductionCompany, related_name='actors')
actor = models.ForeignKey(Actor, related_name='companies')
# your extra fields here
someproperty = models.CharField(max_length=200)
class Meta:
# let's say one per actor
unique_together = ('production_company', 'actor')
This is going to get messy really quickly
We use a F object like this:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson',
productioncompany__film__actor=F('actors__actor'),
actors__someproperty="Plays poker with CEO").distinct()
The tricky part is going to be handling default values (i.e. when there is no value) This would have to be implemented using a custom Manager, but then I am out of my depth.
I will try to explain as well as I can, but it's going to be tricky.
If you want to make a filter on the relationship you may have to do something like this:
def filter_prod(pq_query, someproperty, actor_name):
if someproperty == "Default":
# Great, this means we can ignore the parallel relationship:
return pq_query.filter(productioncompany__film__actor__name=actor_name)
else:
# Here comes the hard part
FilmStudio.objects.filter(productioncompany__film__actor__name=actor_name,
productioncompany__film__actor=F('actors__actor'),
actors__someproperty=someproperty).distinct()
The thing I am trying to illustrate here is that there are two kinds of actor-productioncompany relationships, those with custom field values (non-default), and those without.
Now, you can make a custom getter that looks something like this:
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
def get_actors(self):
# This one is not lazy, so be aware
actors = list(self.actors)
# Get a list of actor IDs
actor_ids = [a.actor_id for a in actors]
for actor in Actor.objects.filter(films__production_company_id=self.id):
if actor.id not in actor_ids:
actors.append(ActorProductionComapny(actor=actor, production_company=self)
actor_ids.append(actor.id)
return actors
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
This should not save the relationship to the database until you call .save() on an instance. You can also add a custom save method that ignores/aports .save() calls where all the values are default. Just remember to check if it is a new instance or not, because you don't want it to cancel a "set back to default" call. You could also make it delete on a "set back to default", but check if you are allowed to do that within .save().
For even more complex queries (mix of default and non-default) you have Q-objects (further down on the page from F objects)
In short, you need to create an extra model to store this extra relational data between Actor and FilmStudio.
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
film_studios = models.ManyToMany(FilmStudio, through='ActorFilmStudio')
class ActorFilmStudio(models.Model):
actor = models.ForeignKey(Actor)
film_studio = models.ForeignKey(FilmStudio)
# define extra data fields here
data1 = models.TextField()
data2 = models.IntegerField()
One way to think about this: the data you're trying to store belongs to an Actor-FilmStudio relation, and is not related in anyway to Film or ProductionCompany.
Your existing ability to retrieve the a set of Actors for a given FilmStudio (or vice-versa) does not necessarily imply you can store relational data belonging to these two models using the models defined in your example.
Keep in mind that each of the models you defined in your example are backed by a table in your database. In the case of Actor.films field, Django creates an extra table to store the many-to-many relationship data.
Since you're looking to store relational data between Actor and FilmStudio, you need to consider where the data will be stored in your database. Can you store the data in the Film model? or the ProductionCompany model?
I have the following User object as a simple example
class AppUser(AbstractBaseUser, PermissionsMixin):
follows = models.ManyToManyField('self', related_name='follows_users', symmetrical=False)
I want to get all the user's that follow a user. for example
u2.follows.add(u1)
u3.follows.add(u1)
I want to return [u2,u3] as a result for users that follow u1
You need to use the related_name you set in the field definition:
users_who_follow_u1 = u1.follows_user.all()
You should also find a better name for it such as followed_by.
i have this problem with my models.
class Message(models.Model):
user = models.ForeignKey(UserProfile)
text = models.TextField(max_length=160)
voting_users = models.ManyToManyField(UserProfile)
def __unicode__(self):
return self.text
and
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
def __unicode__(self):
return self.user.username
I get this error when i try to call message.voting_users:
message: Accessor for m2m field 'voting_users' clashes with related field
'UserProfile.message_set'. Add a related_name argument to the definition for
'voting_users'.
I'm actually new to django and i don't get it how i should use related_name attribute.
As it says, voting_users, needs a related_name argument because it clashes with an already defined related field, message_set (an automagic property created by django for your first ForeignKey, Message.user)
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
You must supply a related name argument to either of your ForeignKey / m2m fields to define a unique accessor for the reverse relationship.
For example, the reverse relationship for the Message model on UserProfile is UserProfile.message_set. If you have two ForeignKey's you're attempting to create two different reverse relationships with the same UserProfile.message_set method.
user = models.ForeignKey(UserProfile, related_name="message_user")
...
# would create a UserProfile.message_user manager.
userprofile.message_user.all() # would pull all message the user has created.
userprofile.message_set.all() # would pull all Messages that the user has voted on.
The problem is that both voting_users and message_set have the same attribute name user. related_name allows you to define an alias for one of the attributes that you can use to avoid name conflicts.
(Edit: Wrong link)
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
This question is very similar to another question listed here:
Django: Why do some model fields clash with each other?