I have an app that has 3 models: Podcasts, Episodes, and Categories.
Categories have a ForeignKey for Podcast:
class Category(models.Model):
...
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
...
So, a podcast can have a bunch of associated categories.
Each episode can be associated with a podcast's category. I've added that as a ManyToManyField:
class Episode(models.Model):
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
categories = models.ManyToManyField(Category)
...
The problem is, in the admin, and modelforms, the categories field shows a list of every category for every podcast, not just the ones associated to this podcast.
How can I use limit_choices_to to only limit categories with the foreign key of the same podcast the episode is related to? I've tried the following, but obviously doesn't work because 'self' is not defined.
categories = models.ManyToManyField(Category, limit_choices_to={'podcast': self.podcast})
I'd like to do this at the model level if possible so I dont have to add additional logic around the rest of the app.
Thanks for any help!
To me it seems like the case when you would normally want to use ManyToMany relationship with an intermediate model (Podcast in this case). Unless you have to keep your relations as they are, consider this solution:
class Category(models.Model):
title = models.CharField(max_length=120)
class Episode(models.Model):
title = models.CharField(max_length=120)
categories = models.ManyToManyField(Category, through='Podcast')
class Podcast(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
episode = models.ForeignKey(Episode, on_delete=models.CASCADE)
title = models.CharField(max_length=120)
https://docs.djangoproject.com/en/3.0/topics/db/models/#extra-fields-on-many-to-many-relationships
Related
This is a question about how to add a field to a many-to-many relationship in Django.
I have a model LandingPage and a model Product. (Code below). In my project, LandingPages can have many Products listed on them and those same Products can appear on multiple different LandingPages.
Product is connected to LandingPage via a ManyToManyField.
My Goal:
I am trying to figure out how to add a field so that I can set the order (1 through 10) for Products on their associated LandingPages. Reminder, Product instances can appear on multiple LandingPages, so each instance will need to have a different order attribute.
Ideally, I'd like to expose this functionality via the built-in Django admin. Right now it shows the relationships table, but not the order field as it does not yet exist. (Screenshots/mockups below).
My Code:
models.py
class LandingPage(models.Model):
"""Stores a single LandingPage and metadata.
"""
name = models.CharField(max_length=200, help_text="The name is only used internally. It is not visible to the public.")
slug = models.SlugField(default="", editable=False, max_length=150, null=False, verbose_name="Slug", help_text="This is not editable.")
# Additional fields that I do not believe are relevant
class Product(models.Model):
"""Stores a single Product and metadata.
"""
name = models.CharField(max_length=200, help_text="Used internally. Not visible to the public.")
active = models.BooleanField(default=False, verbose_name="Product is Live on Landing Pages", help_text="Determines whether the product should be visible on the assocaited landing page or not.")
landing_page = models.ManyToManyField(
LandingPage,
verbose_name="Landing Page",
help_text="The landing page or pages that this product is assocaited with.",
)
# Additional fields that I do not believe are relevant
admin.py
# Inline configuration used by LandingPageAdmin
class ProductInline(admin.TabularInline):
"""Creates Inline table format for displaying Product data."""
model = Product.landing_page.through
extra = 0
class LandingPageAdmin(admin.ModelAdmin):
"""Specifies LandingPage data in Admin."""
readonly_fields=('slug',)
inlines = [ProductInline]
save_as = True
# Inline configuration used by Product Admin
class LandingPageInline(admin.TabularInline):
"""Creates Inline table format for displaying LandingPage data."""
model = LandingPage.product_set.through
extra = 0
class ProductAdmin(admin.ModelAdmin):
"""Specifies Product data in Admin."""
inlines = [LandingPageInline]
save_as = True
Mockups (for clarity):
Current State
Desired State
(I added the desired functionality in red for clarity. The order integers should be editable so that the order can be re-arranged.)
My Question
How can I accomplish this goal of adding an editable order field to this pre-existing relationship?
Should I manually add an order field to the product-landingpage join table that was automatically created by Django? If I do that, is there a way to have the Django admin show that added field?
Or should I go about it a totally different way?
Thank you in advance!
I found the answer to this.
The solution is create an intermediary model and connect it using "through". Example below:
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Official docs are here: https://docs.djangoproject.com/en/3.1/topics/db/models/#intermediary-manytomany
Others in my situation may find it useful to read this question/answer as it does a good job of explaining various solutions: Define an order for ManyToManyField with Django
I've two models as below.
class Category(models.Model):
title = models.CharField(max_length=55)
class Meta:
verbose_name = 'Food Category'
verbose_name_plural = 'Food Categories'
def __str__(self):
return self.title
class FoodItem(TimeStampWithCreator):
CATEGORY_CHOICES = (
('takeway', 'Takeaway'),
('dine_in', 'Dine In'),
('function', 'Function'),
)
type_menu_select = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='takeway')
category = models.ForeignKey(FoodCategory, on_delete=models.CASCADE)
i want to filter all the categories containing takeaway, I've no idea how to achieve this
You've included category choices in your FoodItem model but the model also has a ForeignKey to a Category model, this isn't needed if the only category objects you have are those three choices (the category field must refer to one of those anyway as it's the ForeignKey). To filter the items by category you would need to use a queryset filter.
https://docs.djangoproject.com/en/3.0/topics/db/queries/#retrieving-specific-objects-with-filters
FoodItem.objects.filter(category=YourCategory)
This is usually the kind of thing I'd like to test in the shell as I don't do it very often. If what you want is actually all Category with a FoodItem that have type_menu_select set to 'takeway' then the following should work (but I haven't tested it):
Category.objects.filter(fooditem__type_menu_select='takeway')
This is using the "reverse" relationship on the ForeignKey and there's more information in the Django docs (search for 'reverse').
I have an problem where I can’t decide how to design the models for the following scenario
I want to create a companies table that will hold a list of companies. This table will have a comment field in it
I want that comment field to be able to hold multiple comments that are dated
A company can have multiple comments but a comment can only belong to only one company
Here the Comments table
class Comments(model.Models):
date = models.DateField(auto_now_add=True)
comment_text = models.TextField(required=True)
If I create the Companies table like this;
class Companies(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
comment = models.ForeignKey(Comments, on_delete=models.SET_NULL, null=True)
Then I can only attach one comment to one specific row of the Companies table
If I create the Companies table like this;
class Companies(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
comment = models.ManyToManyField(Comments)
Then a comment can belong to multiple companies which I don’t want.
In the Django documentation https://docs.djangoproject.com/en/2.0/topics/db/examples/ there is only one other options left which is the one-to-one mapping and this is clearly not what I want.
How can achieve what I want ?
You should do this.
class Company(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
class Comment(models.Model):
company = models.ForeignKey(Company, related_name='comments', on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
comment_text = models.TextField()
When accessing comments of a certain company, It will be;
comments = company.comments.all()
Just put the ForeignKey on Comment, pointing to Company. This does exactly what you want.
I have four models in my models.py which are:
models.py
class Course(models.Model):
course_code = models.CharField(max_length=100,unique=True)
title = models.CharField(max_length=200)
short = models.CharField(max_length=50)
elective_group = models.CharField(max_length=100)
class Unit(models.Model):
title = models.CharField(max_length=100)
short = models.CharField(max_length=50)
course = models.ForeignKey(Course)
class Pattern(models.Model):
pattern_name = models.CharField(max_length=200)
class ExamSchedule(models.Model):
exam_date = models.DateTimeField()
course = models.ForeignKey(Course)
pattern = models.ForeignKey(Pattern)
**units = models.ManyToManyField(Units)**
I have all these models register with admin site, so that I can use admin functionality for these models.
My problem is when a user creates or edits a ExamSchedule object, I want the units(field) multivalue widget should contains only those values that are associated with a course as every course can have multiple units. So if user creates an Examschedule object and after selecting a course from dropdown the unit widget should only contains those units that related to the course selected.
Thanks
I have used this django plugin to do this exact thing in the admin sections. I believe it also works in the front end as well:
https://github.com/digi604/django-smart-selects
I have the following Django 1.2 models:
class Category(models.Model):
name = models.CharField(max_length=255)
class Article(models.Model):
title = models.CharField(max_length=10, unique=True)
categories = models.ManyToManyField(Category)
class Preference(models.Model):
title = models.CharField(max_length=10, unique=True)
categories = models.ManyToManyField(Category)
How can I perform a query that will give me all Article objects that are associated with any of the same categories that a given Preference object is related with?
e.g. If I have a Preference object that is related to categories "fish", "cats" and "dogs", I want a list of all Articles that are associated with any of "fish", "cats" or "dogs".
Try:
preference = Preference.objects.get(**conditions)
Article.objects.filter(categories__in = preference.categories.all())
Article.objects.filter(categories__in=myPreferenceObject.categories.all())