Django Circular Model Dependency - python

I have a circular dependency in my Django models, such that model A has a foreign key reference to B, while B has a many-to-many reference to A. I've consulted other SO posts and have used the string model names instead of the actual classes, but to no avail. Here are abbreviated versions of my two classes:
User model
import listings.models
class User(models.Model):
...
favorites = models.ManyToManyField('listings.models.Listing')
Listing model
import users.models
class Listing(models.Model):
...
owner = models.ForeignKey('users.models.User')
Every time I attempt to run syncdb, it outputs the following error:
Error: One or more models did not validate: users.user: 'favorites'
has an m2m relation with model listings.models.Listing, which has
either not been installed or is abstract. listings.listing: 'owner'
has a relation with model users.models.User, which has either not
been installed or is abstract.
How do I resolve this without sacrificing the established relationship?

'listings.models.Listing' should be 'listings.Listing'
'users.models.User' should be 'users.User' (or 'auth.User' if you were to use django.contrib.auth.models.User)
Refer to official documentation for more.

You can just delete your imports because, you're not depend on them on code. You use just string with model name - it's not a dependency.
Also you should delete models - from your strings because you can refer to your model as app_name.model_name

Related

Why Django want to alter table with proxy model?

I'm using Django 1.8, Python 2.7.15. Junior dev.
I created a model Test with many to many field to Django Group model:
class Test(models.Model):
group = models.ManyToManyField(Group)
I've made a migration, my project was working, all was good.
Now I had to create Group's proxy model with specyfic managers:
class ExtendedGroup(Group):
get_all = AllGroupsManager()
objects = DefaultGroupManager()
class Meta:
proxy = True
And I had to change my Test model group field (is has methods that must use new managers... so I have to use ExtendedGroup):
I'm changing ManyToManyField relation from Group to ExtendedGroup.
class Test(models.Model):
group = models.ManyToManyField(ExtendedGroup)
ExtendedGroup is proxy model so I think Test's group field should be related to Group even if I use ExtendedGroup (becouse it is proxy model).
But when I run server I got ProgrammingError:
column test_test_group.extendedgroup_id does not exist
When I "makemigrations" I can see that Django want to Alter group field with new relation to model (ExtendedGroup) that actually does not have any tables in database becouse it is proxy model.
operations = [
migrations.AlterField(
model_name='test',
name='group',
field=models.ManyToManyField(to='mainpage.ExtendedGroup'),
),
]
What is going here? Is it right? Why Django want to create relation to model that does not have any tables?
I just don't understand it.
I will appreciate any help!
EDIT:
When I run migration (migrate) I get a bunch of errors:
AttributeError: 'ManyToManyField' object has no attribute
'm2m_reverse_field_name'
So it does not work :(

Getting data from multiple databases with same tablenames in django

I need to get data from different imported MySQL-databases in Django
(Django 1.11.7, Python 3.5.2).
I run manage.py inspectdb --database '<db>' and then use the models in django.
Until now, I only had to access tables with different names. For this purpose, I used the using keyword in the queryset to specify the appropriate database and then concatenated the result, like this:
from ..models.db1 import Members
from ..models.db2 import Actor
context['db1_data'] = Members.objects.using('db1').filter...
context['db2_data'] = Actor.objects.using('db1').filter...
context["member_list"] = list(chain(
context["db1_data"],
context["db2_data"],
))
return context
Now I have the problem that there are tables with the same model names in two databases. I get the following error when using the above-mentioned method (I substituted the names):
RuntimeError: Conflicting '<table-name>' models in application '<app>': <class '<app>.<subfolder>.models.<db1>.<table-name>'> and <class '<app>.<subfolder>.models.<db2>.<table-name>'>.
I already tried importing the model with a different name, like this:
from ..models.db3 import Members as OtherMembers
but the error still comes up.
Shouldn't from ..models.db1 and from ..models.db2 be clear enough for Django to spot the difference between the two models?
One option would probably be to rename the models themselves, but that would mean to rename every database model with the same names. Since I will use many more databases in the future, this is not an option for me.
I tried from models import db1, db2 and then db1.Members etc., which still raises the error.
I read about the meta db_table = 'dbname.tablename'-option, but since the model is auto-generated through inspectdb, it already has something like this on every class:
class MyModel(models.Model):
<models>
class Meta:
managed = False
db_table = 'my_model'
As described before, the other database has the exact same model and hence the same Meta classes. I can't and don't want to change every Meta class.
EDIT:
My project structure looks like this:
app
-> admin.py
-> ...
-> models.py
-> views.py
subfolder
-> models
-> db1.py
-> db2.py
-> views
-> db1.py
-> db2.py
Assuming that you have set up your multiple databases correctly:
Have you tried to add a Custom Router?
If not follow the example given on the documentation link.
Have you tried to use a Custom Manager for your models?
Create a manager for each model, like this:
class YourModelManagerX(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).using('your_db_X')
And then add it to your appropriate model as the objects field:
class YourModel(models.Model):
...
fields
...
objects = YourManagerX()
class Meta:
managed = False
You may need to try both at once.
If db1.Members and db3.Members have the same definition, you do not have to redeclare the Members class separately for each database.
Models.py
...
class Members(models.Model): # Only declared once ever!
....
then,
from Models import Members
context['db1_data'] = Members.objects.using('db1').filter...
context['db3_data'] = Members.objects.using('db3').filter...
... # continue processing
Shouldn't from ..models.db1 and from ..models.db2 be clear enough for django to spot the difference between the two models?
Django Models are not database-specific, more like schema-specific, so if you have the same table in two different databases, one class extending model.Models suffices. Then, when you attempt to retrieve the objects, either specify the database with using(), or using routers, which you can read about in the Django docs https://docs.djangoproject.com/en/2.0/topics/db/multi-db/#an-example

Fields clash in case of inheritance

I' ve the following simplified model structure:
#common/models.py
class CLDate(models.Model):
active = models.BooleanField(default=True)
last_modified = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
#br/models.py
class Dokument(CLDate):
user = models.ForeignKey(User)
class Entity(CLDate):
dokument = models.ForeignKey(Dokument)
. Both class inherits from CLDate, and i' ve a OneToMany relation between them. When i try to migrate, i got the following error:
python manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
br.Entity.dokument: (models.E006) The field 'dokument' clashes with the
field 'dokument' from model 'common.cldate'.
I can' t really get why is this structure a problem for Django hence the Entity is a totally different object than the Dokument. Could anyone explain me why, and how could i solve it with this structure? So both should inherit from CLDate and there should be this kind of relation between the 2 models from the br application.
I also tried to delete all the migration files, and solve it that way, but the same. Runserver gives also this error.
Django: 1.11.2
Python: 3.4.2
Debian: 8.8
.
Thanks.
If i rename the dokument property name in the Entity model, it works fine.
I' m also almost pretty the same layout was working previously (in previous Django versions).
Since you are using multi-table inheritance, Django creates an implicit one-to-one field from Dokument to CLDate. The reverse relation dokument from CLDate to Dokument is clashing with your Entity.dokument field.
If you don't want to rename your Entity.dokument field, then your other option is to explicitly define the parent link field from Dokument to CLDate and set related_name.
class Dokument(CLDate):
cl_date = models.OneToOneField(CLDate, parent_link=True, related_name='related_dokument')
user = models.ForeignKey(User)

Crossed import in django

On example, i have 2 apps: alpha and beta
in alpha/models.py import of model from beta.models
and in beta/models.py import of model from alpha.models
manage.py validate says that ImportError: cannot import name ModelName
how to solve this problem?
I have had this issue in the past there are two models that refer to one another, i.e. using a ForeignKey field. There is a simple way to deal with it, per the Django documentation:
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:
So in your beta/models.py model, you would have this:
class BetaModel(models.Model):
alpha = models.ForeignKey('alpha.AlphaModel')
...
At this point, importing from alpha.models is not necessary.

What is choice_set in this Django app tutorial?

There is this line in the Django tutorial, Writing your first Django app, part 1:
p.choice_set.create(choice='Not much', votes=0)
How is choice_set called into existence and what is it?
I suppose the choice part is the lowercase version of the model Choice used in the tutorial, but what is choice_set? Can you elaborate?
UPDATE: Based on Ben's answer, I located this documentation: Following relationships "backward".
You created a foreign key on Choice which relates each one to a Question.
So, each Choice explicitly has a question field, which you declared in the model.
Django's ORM follows the relationship backwards from Question too, automatically generating a field on each instance called foo_set where Foo is the model with a ForeignKey field to that model.
choice_set is a RelatedManager which can create querysets of Choice objects which relate to the Question instance, e.g. q.choice_set.all()
If you don't like the foo_set naming which Django chooses automatically, or if you have more than one foreign key to the same model and need to distinguish them, you can choose your own overriding name using the related_name argument to ForeignKey.
Two crucial questions are asked here. First: How is choice_set called into existence. Second: What is it?
For all new developers like me, Let me describe how I made it easy for me. Let me answer the second question first. "What is it", through these 3 words? Model Instance, Objects-set related to that instance, Related_manager.
Models.py from Django tutorial:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Instance:
q = Question.objects.get(pk=3)
# Here q is an instance of model class 'Question'.
c = Choice.objects.get(pk=32)
# Here c is an instance of model class 'Choice'.
'Model Instance' is a single 'ROW' of an entire 'TABLE' of your database
Here, the Question Model is used as a foreign key to the Choice Model. Therefore, all the objects-set related to instance q can be filtered by using:
q.choice_set.all()
Therefore, choice_set here is, all the choices related to the question, that has pk=3.
Now, the answer to the first question needs the third word Related Manager. Django documentation here:-
If a model has a ForeignKey, instances of the foreign-key model will
have access to a Manager that returns all instances of the first
model. By default, this Manager is named FOO_set, where FOO is the
source model name, lowercased. This Manager returns QuerySets, which
can be filtered and manipulated as described in the “Retrieving
objects” section above.
This word (choice_set) can be changed using the 'related_name' parameter in the Foreign_key.
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="choices")
For backward relationtionship through foreign key:
q.choice_set.all()
# If using related_name, then it is same as
q.choices.all()
# All the choices related to the instance q.
For forward relationship:
choice_qs = Choice.objects.all()
choice_qs.filter(question=q)
# Same result as above. All the choices related to instance q.

Categories