I'm facing a problem with django-admin. I have three objects:
Description
Job
Project
And I want to edit the Description directly inside Job and Project.
Here is my model.py:
class Description(models.Model):
short_desc = models.TextField()
long_desc = models.TextField()
class Job(models.Model):
location = models.TextField()
desc = models.ForeignKey(Description)
class Project(models.Model):
name = models.TextField()
desc = models.ForeignKey(Description)
So, conceptually, Description is the parent of Job and Project.
And my admin.py:
class DescriptionInLine(admin.StackedInline):
model = Description
#admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
model = Project
inlines = [DescriptionInLine]
#admin.register(Job)
class JobAdmin(admin.ModelAdmin):
model = Job
inlines = [DescriptionInLine]
Whenever I run the django server, I get the following error:
<class 'admin.DescriptionInLine'>: (admin.E202) 'Description' has no ForeignKey to 'Job'.
I understand why I get the error: django expects the relation to be in the other way.
I also tried replacing ForeignKey by OneToOneField, without any success.
Any idea on how to solve this?
You get this error because inlines are intended to be used in the other direction (See this question).
I think for your usecase you'd better use model inheritance:
class Description(models.Model)
class Meta:
abstract = True
# Abstract is optional but I think for your usecase,
# standalone `Description` does not make any sense.
# If not `abstract`, a one-to-one relation will be implied
# between parent and children
short_desc = models.TextField()
long_desc = models.TextField()
class Job(Description):
location = models.TextField()
class Project(Description):
name = models.TextField()
Related
I am making an app that is pretty much similar to google classroom in django.
I have a Course model and an assignment model, and I want to connect an assignment to the specified course.
These are my models
class Assignment(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
date_created = models.DateTimeField(default=timezone.now)
class Course(models.Model):
title = models.CharField(max_length=100)
subject = models.CharField(max_length=100)
image = models.ImageField(default='no_course_image.jpg', upload_to='course_images')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
students_invited = models.ManyToManyField(User, null=True, blank=True)
assignments = models.ManyToManyField(Assignment, null=True, blank=True)
date_published = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name_plural = 'Course'
ordering = ['-date_published']
def __str__(self):
return '{} - {}'.format(self.title, self.owner)
But i am getting an error when I specify the course field in the assignment model with the ForeignKey!
Could you please help me with how to connect the assignment to the Course model?
Thank you
ForeignKey is used to setup a many to one relationship. As you are trying to setup a ManyToManyField it won't work in this situation as you can see in the Django documentation
ForeignKey¶
class ForeignKey(to, on_delete, **options)¶
A many-to-one relationship. Requires two positional arguments:
the class to which the model is related and the on_delete option.
In fact you don't even need to set the relation in the Assignment Model as Django will take care of creating a third table linking the two together by their primary keys. You can see this in the documentation
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
class Meta:
ordering = ['title']
def __str__(self):
return self.title
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
class Meta:
ordering = ['headline']
def __str__(self):
return self.headline
So every time you add the assignment to the course like so
>>> c1 = Course(title='Python Course')
>>> c1.save()
>>> a1 = Assignment(name='Python Assignment')
>>> a1.save()
>>> c1.assignments.add(a1)
And the relation will automatically be created and c1.assignments.all() will return all the assignments linked to the course
If you need to go the other way around then you would use a1.course_set.add(c1). When using the model that doesn't have the ManyToManyField object tied to it you need to use the *_set notation where * will be replaced by the model name in lower case. Can read more about Related Objects references in the docs here
When you try to create the Model Assignment with reference to the model Course, the Course Model has not yet created and vice versa and you will get an error either of the model is not defined
You can use the quotes for it
class Assignment(models.Model):
course = models.ForeignKey('Course', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
date_created = models.DateTimeField(default=timezone.now)
You can use a custom through model enter link description here
I guess the Course model has to be written before the Assignment model.
I'm try create post with language and content, and relate it on other versions of same page, but I'm get stuck
class Page(models.Model):
content = models.TextField()
language = models.CharField(max_length=7, choices=settings.LANGUAGES)
versions = models.ManyToManyField('self', blank=True)
class Meta:
unique_together = ('language', 'versions',)
This will not work properly, because Django not allow make "unique" ManyToMany fields.
Then I'm try make same relationship trough related model:
class VersionsPage(models.Model):
pass
# ToDo: add unique together here, to foreign key field
class Page(models.Model):
...
versions = models.ManyToManyField('self', blank=True, through="VersionsPage")
Anyone know how to make that without using symmetrical=False?
I think you are looking for something like this:
class Page(models.Model):
pass
class PageVersion(models.Model):
page = models.ForeignKey(Page, related_name='versions')
content = models.TextField()
language = models.CharField(max_length=7, choices=settings.LANGUAGES)
class Meta:
unique_together = ('page', 'language',)
#getting all page versions:
page = Page.objects.get(pk=some_id)
versions = page.versions.all()
I need to choose between Multi Table Inheritance and ABC Inheritance and I am not sure if I can still have separate admins for each subclass. I need all the base class fields and all the fields from the subclass in each subclass admin screen.
I am sorry if this is a stupid question, I am still not even finished with the manual but I have a deadline.
Of course, you can have an admin screen for each of your models, and all the fields from base model would be present on child models.
from django docs:
each model in the hierarchy is a model all by itself
Then, if you have:
models.py
class Service(models.Model):
owner = models.ForeignKey('auth.User')
name = models.CharField(max_length=128)
class VariationAService(Service):
# fields
class VariationBService(Service):
# more fields
You can do something like this:
admin.py
class ServiceAdmin(admin.ModelAdmin):
[...]
class VariationAServiceAdmin(admin.ModelAdmin):
[...]
class VariationBServiceAdmin(admin.ModelAdmin):
[...]
You'll get all fields either way. The difference between the two is that with multi-table inheritance...
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
...you'll get both a Place object and Restaurant object, which can both be edited in the admin, but with abstract base classes...
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Meta:
abstract = True
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
...you'll only get a Restaurant object, which can be edited in the admin.
When I try to syncdb I get the error Menu is not a valid class Name.
How can I resolve that relationship case :
class MenuItem(model.Models)
title = models.CharField(max_length=200)
submenus = models.ManyToManyField(Menu, blank=True, null=True)
class Menu(Container):
links = models.ManyToManyField(MenuItem)
From the Django book:
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:
E.g.:
class MenuItem(model.Models)
title = models.CharField(max_length=200)
submenus = models.ManyToManyField('Menu', blank=True, null=True)
^ ^
Edit:
As Francis mentions (and as is written in the documentation):
It doesn't matter which model has the ManyToManyField, but you should only put it in one of the models -- not both.
One of these models has a many to many, the other one uses Django's reverse relations (https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward)
So how I would set it up:
class Menu(Container):
links = models.ManyToManyField(MenuItem)
class MenuItem(model.Models)
title = models.CharField(max_length=200)
Then when I wanted a MenuItem's Menus:
menu_item_instance.menu_set.all()
I have two models, each has a shared ManyToMany, using the db_table field. But how do I prevent syncdb from attempting to create the shared table, for the second model?
class Model1(models.Model):
othermodels = ManyToManyField('Model2', db_table='model1_model2', related_name='model1_model2')
class Model2(models.model):
othermodels = ManyToManyField('Model1', db_table='model1_model2', related_name='model2_model1')
It's working great in my dev environment, because some of the tables got created piecemeal, as I built it all out. But from an empty database, syncdb throws:
_mysql_exceptions.OperationalError: (1050, "Table 'model1_model2' already exists")
Is there a flag that I'm missing from the second model's field to prevent duplicate table creation? Or am I just doing this entirely wrong?
I also found this solution, which worked perfectly for me :
class Test1(models.Model):
tests2 = models.ManyToManyField('Test2', blank=True)
class Test2(models.Model):
tests1 = models.ManyToManyField('Test1', through=Test1.tests2.through, blank=True)
You don't need to put a ManyToManyField on both sides of the relation. Django will do that for you.
You probably want something more like this:
class Model1(models.Model):
name = models.CharField(max_length=128)
...
class Model2(models.Model):
name = models.CharField(max_length=128)
othermodels = models.ManyToManyField(Model1, through='Model1Model2')
...
class Membership(models.Model):
class Meta:
db_table = 'model1_model2'
model1 = models.ForeignKey(Model1)
model2 = models.ForeignKey(Model2)
When you're working with your models, an instance of Model1 will have a othermodels_set field which is automatically added by django. Instances of Model2 will have othermodels.
class Awesome(models.Model):
one = models.TextField()
class Meta:
# Prevent table creation.
abstract = True
http://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes
It's not what you're looking for, but it's the closest they have I belive. Would it not be simpler to make a view?
Maybe:
class Awesome(models.Model):
one = models.CharField(max_length = 255)
two = models.CharField(max_length = 255)
class AwesomeOne(Awesome):
fieldOne = models.ForeignKey(User, related_name = 'one')
class Meta:
abstract = True
class AwesomeTwo(Awesome):
fieldTwo = models.ForeignKey(User, related_name = 'two')
class Meta:
abstract = True
Then, you can have one table created and override the __getattr__ to block access to the original fields.
In Django ManyToMany are bi-directional by default. The think is that you only have to define it on one model, not on both (and usually you don't need to give a name to the table):
class Model1(models.Model):
othermodels = ManyToManyField('Model2')
class Model2(models.model):
...
That's it. Now syncdb will be happy. For more info: Django docs
The only drawback is, if you use the admin, you will have access to othermodels only in Model1.
Edit
So, if you want to have access to the ManyToMany in both models in the Admin, currently the official solution is to use inlinemodel for the second model. I had also this same problem/need just a few days ago. And I was not really satisfied with the inlinemodel solution (heavy in DB queries if you have a lot of entries, cannot use the *filter_horizontal* widget, etc.).
The solution I found (that's working with Django 1.2+ and syncdb) is this:
class Model1(models.Model):
othermodels = models.ManyToManyField('Model2', through='Model1Model2')
class Model2(models.Model):
othermodels = models.ManyToManyField('Model1', through='Model1Model2')
class Model1Model2(models.Model):
model1 = models.ForeignKey(Model1)
model2 = models.ForeignKey(Model2)
class Meta:
db_table = 'app_model1_model2'
auto_created = Model1
See ticket 897 for more info.
Unfortunately, if you're using South you will have to remove the creation of the app_model1_model2 table in every migration file created automatically.