Django: related_name attribute (DatabaseError) - django

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?

Related

Combine one-to-one and many-to-many (django)

I have a model named "db_connector" and another one "project_site".
A "project_site" can have many to many relation with "db-connector".
But the "project_site" should have one default db-connector.
What is the best design-pattern for this?
Can I combine many to many and one to one like this:
Many to many relation for alternative connectors linked to a project_site and one to one for the default connector linked to a project_site?
The user can specify a db_connector, if the user doesn't specify a database_conector, the default db_connector can be used?
Using this pattern on django causes an error (fields.E305)
Project_site.alternative_db_connectors: (fields.E305) Reverse query name for 'Project_site.alternative_db_connectors' clashes with reverse query name for 'Project_site.default_db_connector'.
HINT: Add or change a related_name argument to the definition for 'Project_site.alternative_db_connectors' or 'Project_site.default_db_connector'.
The project_site models looks like this:
class Project_site(models.Model):
name = models.CharField(max_length=150)
project_group = models.ForeignKey(Project_Group, on_delete=models.CASCADE)
default_db_connector = models.OneToOneField(DB_Connector, on_delete=models.CASCADE , primary_key=True ,)
alternative_db_connectors = models.ManyToManyField(DB_Connector)
def __str__(self) -> str:
return self.name
In SQL terms it should be possible, right?

How to use select_related

How do I use select_related to get the first and last name of the employee class below.
class Employee(models.Model):
"""
Model, which holds general information of an employee.
"""
user = models.OneToOneField(User, related_name='users',
on_delete=models.CASCADE, unique=True)
photo_logo = models.FileField(null=True, blank=True)
Here is how I have implemented my query
emp=Employee.objects.filter(pk=1).select_related('user').values('user_first_name','user_last_name','id')
But I get the following logs after running a print statement in django shell
Cannot resolve keyword 'user_first_name' into field. Choices are: address, address_id, attendance, basic,
Since you need specific fields of user model, you dont need select_related in this case, just use:
emp=Employee.objects.filter(pk=1).values('user__first_name','user__last_name','id')
query.
Note that you shoulduse double underscore __ to perform join.
We should use __ for relation field
emp=Employee.objects.filter(pk=1).select_related(
'user'
).values('user__first_name','user__last_name','id')

What does related_name do?

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='+')

"already exists" error when using ManyToMany relationship between the same two models

class Product( models.Model ):
name = models.CharField(verbose_name="Name", max_length=255, null=True, blank=True)
the_products_inside_combo = models.ManyToManyField('self', verbose_name="Products Inside Combo", help_text="Only for Combo Products", blank=True)
However, I got this error when I tried to put the duplicate values:
From_product-to_product relationship with this From product and To
product already exists.
Screencap of the error.
Each pair (Product, Product) must be unique. This is why you get already exists error.
Behind the scenes, Django creates an intermediary join table to
represent the many-to-many relationship.
What do you want to do is to have many-to-many relationship between two models (nevermind that they are the same) with additional information stored - quantity (so you would have ProductA = 2x ProductB + ....
In order to model this relationship you will have to create intermediary model and use through option. Documentation explains it very well, so have a look:
https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany
Update
Here is minimal working example:
class Product(models.Model):
name = models.CharField(verbose_name='Name', max_length=255, null=True, blank=True)
products = models.ManyToManyField('self', through='ProductGroup', symmetrical=False)
def __str__(self):
return self.name
class ProductGroup(models.Model):
parent = models.ForeignKey('Product', related_name='+')
child = models.ForeignKey('Product', related_name='+')
and admin models:
class ProductGroupInline(admin.TabularInline):
model = Product.products.through
fk_name = 'parent'
class ProductAdmin(admin.ModelAdmin):
inlines = [
ProductGroupInline,
]
admin.site.register(Product, ProductAdmin)
admin.site.register(ProductGroup)
As you can see recursive Product-Product relation is modeled with ProductGroup (through parameter). Couple of notes:
Many-to-many fields with intermediate tables must not be symmetrical, hence symmetrical=False. Details.
Reverse accessors for ProductGroup are disabled ('+') (in general you can just rename them, however, you don't want to work with ProductGroup directly). Otherwise we would get Reverse accessor for 'ProductGroup.child' clashes with reverse accessor for 'ProductGroup.parent'..
In order to have a nice display of ManyToMany in admin we have to use inline models (ProductGroupInline). Read about them in documentation. Please note, however, fk_name field. We have to specify this because ProductGroup itself is ambiguous - both fields are foreign keys to the same model.
Be cautious with recurrency. If you would define, for example, __str__ on Product as: return self.products having ProductGroup with the same parent as the child you would loop infinitely.
As you can see in the screencap pairs can be duplicated now. Alternatively you would just add quantity field to ProductGroup and check for duplication when creating objects.

How can I get all the objects in a Django model that have a specific value for a ForeignKey field?

I have a Model with a Foreign Key of "Parent"
class Item(models.Model):
parent = models.ForeignKey(Parent)
This is the FK model
class Parent(models.Model):
name = models.CharField(blank=True, max_length=100)
def __unicode__(self):
return str(self.name)
I am trying to run a query that gets all Items with a parent of "xyz" I get nothing
Item.objects.filter(parent="xyz")
When I try:
Item.objects.filter(parent.name="xyz")
Or:
Item.objects.filter(str(parent)="xyz")
I get an error:
SyntaxError: keyword can't be an expression
What is the proper way to do this?
You can use a double underscore in the keyword passed to filter() to access fields in a foreign key relationship. Like this:
Item.objects.filter(parent__name="xyz")
Django documentation
http://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships
Just for future reference for Googlers, with recent versions of Django you have to use an additional method in the keyword. For example, instead of parent__name you have to do parent__name__exact. Cato's link contains other examples.