Django models how to fix circular import error? - python

I read about a solution for the error (write import instead of from ...) but it doesn't work I think because I have a complex folder structure.
Directory structure
quiz/models.py
import apps.courses.models as courses_models
class Quiz(models.Model):
lesson = models.ForeignKey(courses_models.Lesson, on_delete=models.DO_NOTHING) # COURSE APP MODEL IMPORTED
courses/models.py
import apps.quiz.models as quiz_models
class Lesson(models.Model):
...
class UserCompletedMaterial(models.Model):
...
lesson = models.ForeignKey(Lesson)
quiz = models.ForeignKey(quiz_models.Quiz) # QUIZ APP MODEL IMPORTED
How you can see I just can't keep it together or something else..
Because I think the UserCompletedMaterial model is a part of courses app

Both models refer to each other, and this thus means that in order to interpret the former, we need the latter and vice versa.
Django however has a solution to this: you can not only pass a reference to the class as target model for a ForeignKey (or another relation like a OneToOneField or a ManyToManyField), but also through a string.
In case the model is in the same application, you can use a string 'ModelName', in case the model is defined in another installed app, you can work with 'app_name.ModelName'. In this case, we thus can remove the circular import with:
# do not import the `courses.models
class Quiz(models.Model):
lesson = models.ForeignKey(
'courses.Lesson',
on_delete=models.DO_NOTHING
)
# …

Related

Inheritance model update to its parent model

I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.

Proper way to log Django User activity

What's the proper way to log user activity in Django?
For example let's say a user has Groups, and I'd like to keep a record of when Object1 has been added or removed.
The method that comes to mind is to create a new record every time and pull the latest record but this feels wrong (and causes some filtering problems, eg: you can't just filter on is_member=True since you'll get stale results). Is there a proper way to log these in Django?
You can use django-auditable-models for that. It will hook in the django workflow, and will avoid that you have to write all logic yourself.
You can use loggers in Django.
https://docs.djangoproject.com/en/1.8/topics/logging/#topic-logging-parts-loggers
To log something like that I recommend you to create an core app with a TimeStampModel model:
from django.db import models
from django.utils.timezone import now
class TimeStampModel(models.Model):
"""
TimeStampModel class allows us to follow creation and update of each inherit instance
"""
created_at = models.DateTimeField(auto_now_add=now(), editable=False)
updated_at = models.DateTimeField(auto_now=now(), editable=False)
class Meta:
abstract = True
Now, inherit each models from TimeStampModel that you want to record creation or update date.
E.g:
from django.db import models
from core.models import TimeStampModel
class Token(TimeStampModel):
uuid = models.CharField(max_length=255, primary_key=True)
# ...
You can also add a delete attribute (Boolean) to realize logical delete. And the last update, will be the date of deletion.
Two Scoops of Django 1.8 recommends also this practice.

django models relation to two models before saving

I have two apps say app1 and app2 and I have models in it.
from app2.models import SecondModel
class FirstModel(models.Model):
first_field = models.ManyToManyField(SecondModel, blank=True)# or Foreign Key
from app1.models import FirstModel
class SecondModel(models.Model):
second_field = models.ForeignKey(FirstModel)
When I do this I get import error.
Could not import name 'FirstModel'
Why is this happening ?
The error is because you have a circular import. It's not possible to for both modules to import from each other.
In this case, you don't need to import the models into each app. Remove the imports, and use a string app_label.ModelName instead.
# app1.models.py
class FirstModel(models.Model):
first_field = models.ManyToManyField('app2.SecondModel')
# app2.models.py
class SecondModel(models.Model):
second_field = models.ForeignKey('app1.FirstModel')
there is a name conflict here .. you defined the FirstModel in your models.py and then defined FirstModel, from the code above, this could be the possible problem. Also, the import error generally mean, there is no FirstModel defined from where you are importing it.
However, a more generic way of doing FKs without import is generally
class FkModel(models.Model):
relationship = models.ManyToManyField('appName.modelName')
where appName is the app from where you are trying to import the model from, and modelName is the model to which you are trying to create the relationship. This helps where you are trying to do something like this.
Lets say your app name is 'app' and you are trying to create a many to many relationship from 1st model to a 2nd model for which the class is declared after the 1st model e.g.
class Model1(models.Model):
first_field = models.ManyToManyField('app.Model1')
class Model2(models.Model):
name = models.CharField(maxlength=256)
that is just put your appname.modelName inside strings :)
also, you have a flaw in your ManyToManyField() declaration i.e. you don't need to define blank in Many to Many. The way db's work under the hood is, they create a 3rd database table just to store many to many relationships.
hope it helps
//mouse.

Django: Get related model class from parent model class

In Django 1.7, I couldn't quickly find a simple one-liner to get a related model class from the parent model.
Often these two models are in different files and one already imports the other leading to circular (i.e. broken) imports.
Here's a simple example:
# File: classroom_model.py
from django.db import models
class Classroom(models.Model):
class_code = models.IntegerField()
# File: student_model.py
from classroom_model import Classroom
class Student(models.Model):
classroom = models.ForeignKey(Classroom, related_name="student_set")
```
Here, a desire could be to gain access to the Student model class in Classroom, for example, to write a #classmethod that creates students (e.g. classroom.create_student(name).
Two ways:
1) Simplest way is to change the reference in the ForeignKey definition from a direct model reference to a string, e.g.:
classroom = models.ForeignKey("Classroom")
then remove the import line:
from classroom_model import Classroom
2) The other way is to define in classroom_model.py that:
Student = Classroom.student_set.related.model```
This would be used inside a method where it is used.

Django model names are case insensitive, right?

If I have myapp/models.py
from django.db import models
class FooBar(models.Model):
x = models.BooleanField()
class Foobar(models.Model):
y = models.BooleanField()
and add myapp to INSTALLED_APPS and do a syncdb, I only get FooBar model converted to a db table. The Foobar model is ignored.
Another strange thing to note is that when we do
from myapp import models
both FooBar and Foobar are present as attributes of models. However,
>>> models.FooBar.__name__
'FooBar'
>>> models.Foobar.__name__
'FooBar'
and both are just interfaces to the db table of FooBar (by default myapp_foobar).
I am asking this question because it seems to me from this that django model names are case insensitive and yet I have not found any documentation stating this and moreover this question was answered to the effect that django model names are case sensitive.
Django model names are not case insensitive, but basically, Django creates a lowercase table name from the app and model names. Thus FooBar, which is in myapp, will generate a myapp_foobar table, and so will Foobar.
Obviously in your example, the names will overlap. Django should warn about clashing table names, but instead, silently ignores clashing models.
To fix this, I suggest you have explicit and distinct table names for your two models. Use the Meta inner class, and set Meta.db_table. See the Django documentation on models Meta.
Example given:
class FooBar(models.Model):
x = models.BooleanField()
class Meta:
db_table = 'myapp_foobar_one'
class Foobar(models.Model):
x = models.BooleanField()
class Meta:
db_table = 'myapp_foobar_two'
In a real world environment, I would never have two similar model names in the same app. It is extremely error-prone.

Categories

Resources