Django: how to set ForeignKey related_name in Abstract Model class? - python

I want to create on Abstract Model class for future inheriting like this:
class AbstractModel(models.Model):
created_at = models.DateTimeField(
auto_now_add=True,
blank=True,
null=True,
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='XXX_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Field 'created_at' is working fine, but how to generate related_name in 'created_by' for my child classes to prevent clashing?

As the Be careful with related_name and related_query_name section of the documentation says, you can:
To work around this problem, when you are using related_name or related_query_name in an abstract base class (only), part of the value should contain '%(app_label)s' and '%(class)s'.
'%(class)s' is replaced by the lowercased name of the child class that the field is used in.
'%(app_label)s' is replaced by the lowercased name of the app the child class is contained within. Each installed application name must be unique and the model class names within each app must also be unique, therefore the resulting name will end up being different.
You thus can work with:
class AbstractModel(models.Model):
# …
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='%(class)s_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Then the related_name will be foo_created_by if the name of the model that inherits is named foo.
Or if the same model name can occur in different apps:
class AbstractModel(models.Model):
# …
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='%(app_label)s_%(class)s_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Then the related_name will be bar_foo_created_by if the name of the model that inherits is named foo in an app named bar.

Related

How to fix manytomany field in django

How to make a one to many relationship in Django/Mysql?
I have an identical situation to this post, yet, my django returns errors on the admin page:
get() returned more than one order2pizza-- it returned 5!
order2pizza with that pizza already exists.
My mysql database have composite keys on a tertiary table to order and pizza to link multiple pizzas to an order.
models.py:
class Orders(models.Model):
order_id = models.AutoField(primary_key=True)
order_name = models.CharField(max_length=100, blank=True, null=True)
class Pizza(models.Model):
Pizza= models.AutoField(primary_key=True)
Pizza_name= models.CharField(max_length=50, blank=True, null=True)
class order2pizza(models.Model):
order = models.ManyToManyField(Orders, models.DO_NOTHING, )
pizza_id= models.IntegerField()
class Meta:
unique_together = (('order ', 'pizza_id'),)
A many-to-many relation can be expressed in two ways. First, you can manually specify a "join" model, like this:
class Orders(models.Model):
order_name = models.CharField(max_length=255, blank=True)
class Pizza(models.Model):
Pizza_name= models.CharField(max_length=255, blank=True)
class Order2Pizza(models.Model):
order = models.ForeignKey(Order, models.CASCADE)
pizza = models.ForeignKey(Pizza, models.CASCADE)
class Meta:
unique_together = ['order ', 'pizza']
This is useful if you want to put extra fields on the Order2Pizza model. A field named quantity would be very useful in your example.
The second option is to use a ManyToManyField. This will automatically create the join model for you:
class Orders(models.Model):
order_name = models.CharField(max_length=255, blank=True)
pizzas = models.ManyToManyField('Pizza', related_name='orders')
class Pizza(models.Model):
Pizza_name= models.CharField(max_length=255, blank=True)
In your original question you put the ManyToManyField on the Order2Pizza model, which is nonsensical.
However, the source of your bug is probably your manual inclusion of several *_id fields. Don't do that. They will always be created automatically by Django and you should never have to specify them manually. Instead, try the two options above and see how they work.

Add a foreign key to AbstractUser

I have this two models:
class User(AbstractUser):
pass
class Listing(models.Model):
listing_id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
title = models.CharField(max_length=64)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="listing")
I would like to do the following in the User model:
class User(AbstractUser):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
But i'm getting the following error:
NameError: name 'Listing' is not defined
Is it possible to add a foreign key to abstractuser?
Since you define User before Listing, the name is not yet defined. This is however not a problem for Django since you can use a string literal as well:
class User(AbstractUser):
listing = models.ForeignKey('Listing', on_delete=models.CASCADE)
class Listing(models.Model):
listing_id = models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID'
)
title = models.CharField(max_length=64)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='listing'
)
You can however not define a ForeignKey with the name listing here, since you already have an ForeignKey in your Listing model with as related_name='listing'. This would thus result in an error. You thus should either change the related_name, or rename the listing in the User model.
Pass the model name as a string to avoid issues where you want to reference a model that is not yet defined
class User(AbstractUser):
listing = models.ForeignKey('Listing', on_delete=models.CASCADE)
You can also use the same method to refer to a model in another app by adding the app name
listing = models.ForeignKey('app.Listing', on_delete=models.CASCADE)

Add model attribute to same model in Django

I have a model called Company. The Company could be the child of a bigger company. So in the model Company should be a attribute "parent" that is also a Company.
I got this:
class Company(models.Model):
name = models.CharField(max_length=250)
parent = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name="notification",
null=True,
blank=False,
)
But django is always saying I need to create a Company class. Is this the right way to do this?
Use 'self' keyword to reference the same model.
class Company(models.Model):
name = models.CharField(max_length=250)
parent = models.ForeignKey(
'self',
on_delete=models.SET_NULL,
related_name="notification",
null=True,
blank=False,
)

Django ManyToMany Field with an already existing table

What I'm trying to achieve is, having model Person that is created and managed by Django have a ManyToMany field with model Property that was "created" using inspectdb and already exists in the database.
(Property contains Geographical data and cannot be managed or changed by Django)
When trying to migrate, it raises :
ValueError: Related model 'cadastroapp.Property' cannot be resolved
Full stack here
Worth nothing that I removed from the migration file the step to create model Property, since it already exists and AFAIK there's no way to tell Django this in the model Class
models.py (simplified) :
class Person(models.Model):
objectid = models.AutoField(primary_key=True)
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
class Meta:
db_table = 'django_person'
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
class Meta:
db_table = 'django_person_property'
class Property(models.Model):
objectid = models.BigIntegerField(unique=True, primary_key=True)
created_user = models.CharField(max_length=765, blank=True, null=True)
created_date = models.DateTimeField(blank=True, null=True)
last_edited_user = models.CharField(max_length=765, blank=True, null=True)
last_edited_date = models.DateTimeField(blank=True, null=True)
shape = models.TextField(blank=True, null=True) # This field type is a guess. - ESRI Shape
class Meta:
managed = False
db_table = '"GEO"."PROPERTY"'
There are a couple errors in your models.py file.
When defining a Foreignkey or ManytoMany field, you don't want the model name to be in quotes.
Please change:
class Person(models.Model):
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
to:
class Person(models.Model):
properties = models.ManyToManyField(
Property,
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey(Person, on_delete=models.CASCADE)
cod_property = models.ForeignKey(Property, on_delete=models.CASCADE)
then delete your migration file cadastroapp.0006_auto_20161122_1533.
then run makemigrations and migrate again.
This may still not migrate without errors, but it will get us on the right track.
I think that you want to put the model name in quotes. In case you leave it without quotes you have to ensure that the model is defined before the ManyToMany field has been defined. So you will need to have first class Property and then class Person in your file. When you put model name as "Property" then you do not need to care about order of class definitions.

Multiple foreign key fields in abstract Django class

I have an abstract base class that declares two foreign key fields to the user model:
class BaseModel(models.Model):
updated = models.DateTimeField(null=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="updated_by")
created = models.DateTimeField(null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="created_by")
class Meta:
abstract=True
I have multiple classes that inherit from this class. When I run makemigrations, I get the following error for each possible class-pair and for both created_by and updated_by:
myapp.ClassA.updated_by: (fields.E305) Reverse query name for 'ClassB.updated_by' clashes with reverse query name for 'ClassB.updated_by'.
HINT: Add or change a related_name argument to the definition for 'ClassA.updated_by' or 'ClassB.updated_by'.
Even though I already have a related_name set. It works fine with just one of the two foreign key fields declared.
Is it possible to have two foreign key fields to the same model in an abstract class, and if so, how do I set it up?
This is the expected behavior as mentioned in the documentation.
To work around this problem, when you are using related_name in an abstract base class (only), part of the name should contain '%(app_label)s' and '%(class)s'.
class BaseModel(models.Model):
updated = models.DateTimeField(null=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="updated%(app_label)s_%(class)s_related")
created = models.DateTimeField(null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="created%(app_label)s_%(class)s_related")
class Meta:
abstract=True
Since you use the related_name more than once, in model classes you inherit, then related name for the user model is not clear and clashes.
You will have to set a different related_name for each model.

Categories