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.
Related
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
)
# …
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.
I have some models defined in Django such as these:
class Post(models.Model):
text = models.CharField(max_length=50)
class Thread(models.Model):
title = models.CharField(max_length=50)
And I would like to have an external function that, called inside either of these classes, does something related to the class on which it was called.
For example, a ListAll() function that, called inside Post(), lists all Post objects, but called inside Thread, lists all Thread objects.
How would I do this? I've seen replies using __this__ but apparently that references specific class instances, which confuses me a little.
Thank you.
This is a job for a mixin, which can be added to any class.
class ListAll:
def list_all(self):
return self.__class__.objects.all()
Add it to the class:
class Post(ListAll, models.Model):
...
And use it:
my_post_obj.list_all()
I hope this is an example though, as it would be far better to just pass the model class itself into wherever you want to list the objects.
You can use functions inside the model and those can be called with class objects
class Thread(models.Model):
title = models.CharField(max_length=50)
def listAll(self):
return self.objects.all() # a function that would return all objects
As per comments If you have requirements of using a single function for many models then you can load a model dynamically by its name. I won't say that its recommended or anything but it works perfectly.
import importlib
model_module = importlib.import_module('app_name.models.models')
# remember this example refers to loading a module not its class so if you
# have a file models.py containing all the models then this should be perfect
model_object = model_module.model_name # now you have loaded the model
data = model_object.objects.all() # apply whatever you want now
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.
How can I use two models with OneToOne relation in one form using the CreateView in Django 1.5?
My models are these:
class Act(models.Model):
name = models.CharField()
class DetailAct(models.Model):
detail = models.CharField()
act = models.OneToOneField(Act)
My forms
class ActForm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput())
class Meta:
model = models.Act
class DetailActForm(forms.ModelForm):
detail = forms.CharField(widget=forms.TextInput())
class Meta:
model = models.DetailAct
Thank you
You can use two Form objects in one <form> tag without problems. Just make sure that you pass prefix="form-1" to one of the forms (or both - as long as the prefixes are different) in your view. See this answer for an example.
Nope, you can't use built-in class based views for this. Or, at least, not on the high-level you'd expect. You can make your own view class or mixin that will work with two forms, but AFAIK Django doesn't provide one.