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.
Related
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.
I'm having a problem when using multi-table inheritance in Django and I didn't find something that solved it.
I have these two models:
class Person(models.Model):
id = models.CharField(primary_key=True, max_length=12, default="")
name = models.CharField(max_length=12, default="")
birthday = models.DateField()
class Parent(Person):
work = models.CharField(max_length=70, default="")
spouce_field = models.OneToOneField(Person, on_delete=DO_NOTHING, related_name="spouce_field")
And I get this error when running python3 manage.py makemigrations:
ERRORS:
family.Parent.spouce_field: (models.E006) The field 'spouce_field' clashes with the field 'spouce_field' from model 'person.person'.
Any idea what am I doing wrong?
I believe mrcai have answered your question here:
Mark your class Person as an abstract class to avoid field clashing.
You can specify Profile is an abstract class. This will stop the check
from being confused with your parent fields.
class Meta:
abstract = True
I have two models (Slot, Appointment). On the appointment one, I have defined a OneToOne relation with the slot model, so good so far; thanks to the RelatedManager I can access the appointment from the slot object but this's done at python level and I need (for some future changes) appointment_id column to be created on the Slot table.
class Slot(models.Model):
start_at = models.DateTimeField()
end_at = models.DateTimeField()
duration = DateTimeRangeField(
null=True,
blank=True
)
# Bidirectional
appointment = models.OneToOneField(
"appointments.Appointment",
on_delete=models.SET_NULL,
related_name="slot",
blank=True,
null=True
)
class Appointment(models.Model):
slot = models.OneToOneField(
Slot,
on_delete=models.SET_NULL,
related_name="appointment",
null=True,
blank=True,
)
The code above will raise some errors when trying to create the migrations like:
appointments.Appointment.slot: (fields.E302) Reverse accessor for 'Appointment.slot' clashes with field name 'Slot.appointment'.
HINT: Rename field 'Slot.appointment', or add/change a related_name argument to the definition for field 'Appointment.slot'.
Basically, due to the python part of the ORM, you can't define the same field on both models.
Any idea of how to achieve this. I guess I will have to overwrite some parts of the RelatedManager. I could make this throw SQL but then it won't be clear on the code level.
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.
I'm trying to make a dynamic relation between models but I'm just starting with Django.
It's a simple task assignment apps. In this example I've 3 models :
class A(models.Model):
name = models.CharField(_(u'Name'), max_length=255)
class B(models.Model):
name = models.CharField(_(u'Name'), max_length=255)
class Task (models.Model):
parent_A = models.ForeignKey(A, verbose_name=_(u'Parent A'), null=True, blank=True, related_name = "%(app_label)s_%(class)s_parent_A")
parent_B = models.ForeignKey(B, verbose_name=_(u'Parent B'), null=True, blank=True, related_name = "%(app_label)s_%(class)s_parent_B")
but now the problem is, if I want 5 models to be able to be parent of Task, I will need to implement 5 foreign key fields ... There is a way in Django to set up something like that ?
Cheers
Generic relations are what you want.