I am very new to Django.
Can you please give a boilerplate of models how to relate two models between each other.
--Below is Section model
from articles.models import Article
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
article = models.ForeignKey(Article) #Article
--Below is Article model
from sections.models import Section
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner =models.ForeignKey(User, null=False)
sections = models.ManyToManyField( Section )
However. I got the below error:
ValueError: Cannot create form field for 'article' yet, because its related model 'articles.models' has not been loaded yet
Thanks All
B
Breaking cyclic imports
You defined a cyclic import: one module first has to import the other module, but the other module fist has to implement that module, so you defined a cycle.
In Django, one does not per se has to use a class reference to make ForeignKeys, one can use strings that refer to the correct model. In that case the Django framework, will later resolve these.
So we can break the cycle, for example with:
# sections/models.py
# no import from articles
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
# we use a string literal
article = models.ForeignKey('articles.Article', on_delete=models.CASCADE)
and then in the articles/models.py:
# articles/models.py
from sections.models import Section
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner = models.ForeignKey(User, null=False)
sections = models.ManyToManyField(Section)
So here we no longer import articles/models.py in the sections/models.py, and thus we break the cyclic import.
Note that you need to specify an on_delete for a ForeignKey, for example models.CASCADE.
Django's reverse relations
For this specific application however, it seems that you make a double relation between Section and Article, that basically is one relation, you should not do that, Django automatically writes the reverse relation, what you probably want to do, is give it a proper name, for example:
# sections/models.py
# no import from articles
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
# we use a string literal
article = models.ForeignKey(
'articles.Article',
on_delete=models.CASCADE,
related_name='sections'
)
and for articles/models.py:
# articles/models.py
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner = models.ForeignKey(User, null=False)
# no relation to section
Here we can obtain all Sections that relate to some_article with some_article.sections.all().
Related
I'm getting a 'circular import' error when trying to makemigrations in Django. The two models in question are these. The error is being flagged on Team.
from django.db import models
from django.contrib.auth.models import User
from footballapi.models.team import Team
from footballapi.models.bio import Bio
class Player(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.ForeignKey(Bio, on_delete=models.CASCADE)
teams = models.ManyToManyField(Team, on_delete=models.CASCADE, related_name="members")
from django.db import models
from footballapi.models.player import Player
class Team(models.Model):
name = models.CharField(max_length=50)
roster_spot = models.ForeignKey(Player, on_delete=models.CASCADE)
I think the issue is with the ManyToManyField, and I keep reading that I should use a string instead of the import. But I've tried every combination of words and can't find the right string. What should it be? By the way, these models are all from the same app.
Besides the classes you can also use a string "app_name.model_name" for a foreignkey relationship thus avoiding to import each other.
teams = models.ManyToManyField("app_name.Team", on_delete=models.CASCADE, related_name="members")
Alternatively, you could change your datamodel with a through table in your m2m relationship. Using this table you could set a boolean indicating if a player is playing roster_post. Check out the docs: https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ManyToManyField.through
I need to display multiple models in django admin change list view. I want to use single search box to filter all of them at once. Is there an easy way to do it?
My idea was to inherit from admin site, add another view to it and iterate over models in modified change_list.html but i can't import models and ModelAdmins because i get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. error so i can't get the same context that django uses to render regular change_list.html.
What's the correct way to do it? Is there simpler approach?
As Ohad suggested, the most robust approach is probably to make formal relationships between the models from which you want the objects to display together. You have a couple of options here. Essentially you will want to make a master class and then subclass your models from it. This makes a lot of sense if your models are ontologically related to a parent concept. For example:
Publication
Book
Magazine issue
Books and magazines are both publications. They both share some fields, like title and publication date. But they differ in that a book usually has a single author and a magazine has volumes and issue dates. Django already provides a couple different approaches to subclassing using Model inheritance. However, after trying these myself I found that the django-polymorphic extension is way better. Here is a code example of a Django 3.0 app using django-polymorphic which has a Book model and a Magazine model with a single listing of all publications that shows all of the books and magazines in the system.
models.py
from django.db import models
from polymorphic.models import PolymorphicModel
class Publication(PolymorphicModel):
title = models.CharField(max_length=256)
publication_year = models.IntegerField()
class Book(Publication):
author_first = models.CharField(max_length=256)
author_last = models.CharField(max_length=256)
class Magazine(Publication):
volume_number = models.IntegerField()
issue_name = models.CharField(max_length=256)
admin.py
from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
from .models import Publication, Book, Magazine
class PublicationChildAdmin(PolymorphicChildModelAdmin):
""" Base admin class for all child models """
base_model = Publication # Optional, explicitly set here.
#admin.register(Book)
class BookAdmin(PublicationChildAdmin):
base_model = Book # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'author_first', 'author_last')
#admin.register(Magazine)
class MagazineAdmin(PublicationChildAdmin):
base_model = Magazine # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'issue_name')
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year')
This will of course only display those fields that are common (in the Publication model). If you want to display fields that are particular to each model there are various tricks for this. Here's one quick way to do it:
admin.py
...
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year', 'issue', 'author')
def author(self, obj):
if obj.polymorphic_ctype.model == 'book':
book = Book.objects.get(pk=obj.pk)
return book.author_first + ' ' + book.author_last
return None
def issue(self, obj):
if obj.polymorphic_ctype.model == 'magazine':
return str(Magazine.objects.get(pk=obj.pk).issue_name)
return None
Tada!
From the docs it seems that there is no easy solution.(if there is no relation between the models)
https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
So if the search is commonly used build a special model/models that combines the data that might be searched
I was told that doesn't make sense as managers operate on all rows not a single instance but I see what I want to achieve done in django-taggit library.
Here: https://github.com/alex/django-taggit/blob/master/taggit/managers.py
And the the installation works as follows:
from django.db import models
from taggit.managers import TaggableManager
class Food(models.Model):
# ... fields here
tags = TaggableManager()
Then to tag anything, one can simply do the following:
apple.tags.add("red", "green", "fruit")
Note: apple not Apple.
Yet, when I try to do it myself, I get: AttributeError: Manager isn't accessible via MyModelName instances!
My code:
from django.db import models
class TagManager(models.Manager):
def add(self, *tags):
print("Testing...")
class List(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=500)
tags = TagManager()
Then I am trying to call the add method as follows:
l = List.objects.create(...)
l.tags.add(...)
How can I make it work?
I know what ForeignKeys and OneToOneFields are, as well as ManyToManyField, how they work, and when to use them. However, I am working with a project, whose Many part of the relation cannot be modified. So, suppose I want to let a user have many phone numbers, I would normally do this:
# my_app/models.py
from django.db import models
class User(Model):
...
class PhoneNumber(models.Model):
user = models.ForeignKey(User)
The problem I have is that my PhoneNumber model equivalent is from a third-party package, already populated with records, and not subclassed in my own app. That is
# third_party_django_package/models.py
from django.db import models
class PhoneNumber(models.Model):
# This cannot change
# my_app/models.py
from django.db import models
from third_party_django_package.models import PhoneNumber
class User(Model):
# These do not work -- a user can have more than one phone number
phone_number = models.ForeignKey(PhoneNumber)
phone_number = models.OneToOneField(PhoneNumber)
# This is close, but I want a phone number to belong to only one User
phone_numbers = models.ManyToManyField(PhoneNumber, related_name=...)
def clean(self):
# Validating the M2M relation costs extra queries, is slow, and
# is prone to race conditions
This is all pseudocode.
Without using yet another third-party package that accesses Django's internal members, which makes the project even less forwards-compatible, what options do I have left to achieve a proper OneToManyField with the correct schema-level constraints?
You could create another intermediate model, then make phone number OneToOneField to that model, then in that model you define User as ForeignKey.
class UserPhoneNumber(models.Model):
phone_number = models.OneToOneField(PhoneNumber)
user = models.ForeignKey(User)
It's a little cumbersome, but at least it achieves what you need.
Edit:
As #Daniel said, it's possible to do this using m2m relationship with through model, with unique_together on the fields:
class User(Model):
phone_numbers = models.ManyToManyField(PhoneNumber, through=UserPhoneNumber)
class UserPhoneNumber(Model):
phone_number = models.ForeignKey(PhoneNumber)
user = models.ForeignKey(User)
class Meta:
unique_together = ('phone_number', 'user')
This will make your life easier if you want to look up on user's phone numbers by doing numbers = user.phone_numbers.all().
It is possible to make a ForeignKey to more than one model. I want to choose from different models like Parts and Machines Model.
I read this to combine multiple models into one list: How to combine 2 or more querysets in a Django view?
How can I get foreign key to that list somehow?
I know that you asked this over year ago, but I had a similar problem and I want to share a link to the solution for future readers.
Generally the contenttypes framework solves this problem, and I guess this is what Daniel Roseman was talking about.
How to use dynamic foreignkey in Django?
You need generic relations.
A generic relation allows you to dynamically the target model of the foreign key.
I'll provide a comprehensive answer for this question, I know its quite old, but it's still relevant.
We're gonna be using Generic Relations.
First, in settings.py make sure that django.contrib.contenttypes is included in the INSTALLED_APPS array.
Let's create a new model in models.py:
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
With content_type we can associate Image with any other model class, while object_id will hold the other model instance.
class Image(models.Model):
image = models.ImageField(
upload_to="imgs/products", blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
To refer back to the Image model from a Company instance we need to make a reverse generic relation
class Company(models.Model):
name = models.CharField(max_length=100)
images = GenericRelation(Image)
In schema.py, we can create Images in a Company instance like:
company_instance = Company(name="Apple")
company_instance.save()
for img in imgs:
#Image(image=img, content_object=company_instance)
company_instance.images.create(image=img)
company_instance.images.all() # fetch all images
the company_instance.images field is just a GenericRelatedObjectManager (docs)
This is how the final Image table looks in the database:
The Django-polymorphic library provides a simple solution that should work well with the admin and forms too using formsets.
For example:
from polymorphic.models import PolymorphicModel
class BookOwner(PolymorphicModel):
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class StaffBookOwner(BookOwner):
owner = models.ForeignKey(Staff, on_delete=models.CASCADE)
class StudentBookOwner(BookOwner):
owner = models.ForeignKey(Student, on_delete=models.CASCADE)
With this, you can use the parent model to set the owner to either a Staff or Student instance or use the child models directly.