Django - How to dynamically restrict category of blog post? - python

I'm working on a blog project. I want to set primary and secondary categories for the posts. For example,
primary category: Music, Movie, Computer
and secondary categories for each primary category like:
Music - Dance, Rock, Country /
Movie - Script, Teaser, Review /
Computer - Hardware, Software /
And when I create a new post I want to restrict secondary category choices according to the primary category I chose.
(to be precise, in the post creation form,
first, I want both primary and secondary categories to be shown in dropdown menus, and
second, after I choose a primary category I want only the secondary category choices which belong to the primary category I chose to be shown in the dropdown menu for secondary category.)
Currently my models.py:
class PrimaryCategory(models.Model):
title = models.CharField('Primary Category', max_length=50)
class SecondaryCategory(models.Model):
title = models.CharField('Secondary Category', max_length=50)
primary = models.ForeignKey(PrimaryCategory,on_delete=models.CASCADE)
class Post(models.Model):
title = models.CharField(max_length=256)
content = models.TextField()
create_date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
primary_category = models.ForeignKey(PrimaryCategory, on_delete=models.CASCADE, null=True)
secondary_category = models.ForeignKey(SecondaryCategory, on_delete=models.CASCADE, null=True)
and I searched and I can maybe use ForeignKey.limit_choices_to in my ModelForms, but I'm stuck here. Could anybody kindly help writing my forms.py?
currently I only have forms.py like:
from django import forms
from myblog.models import Post,PrimaryCategory,SecondaryCategory
class PostForm(forms.ModelForm):
secondary_category = models.ForeignKey(
PrimaryCategory,
on_delete=models.CASCADE,
limit_choices_to= ??? ,
)
class Meta:
model=Post
to summerize:
how to show category choices in dropdown menu instead of free text input?
how to dynamically restrict secondary category choices according to primary category choice in the form?
(Added) 3. I have no working knowledge of javascript, so I want to do it without JS if possible..If it is not possible to change choices dynamically, then is it at least possible to make the form to give me an error message if secondary category choice I made is not under chosen primary cagetory? I think I'll work on it but any advice would be of great help.
I would appreciate any help. Thank you.

I googled and found exactly what I wanted here:
https://simpleisbetterthancomplex.com/tutorial/2018/01/29/how-to-implement-dependent-or-chained-dropdown-list-with-django.html
It needs jQuery and I actually didn't fully understand what's happening in the jQuery part of the solution, but otherwise everything's clear and it works perfect!

Related

How do I limit a django model's field choices using one of the previous fields?

The following is in my models.py:
class SensorType(models.Model):
hardware_type = models.CharField(max_length=100)
is_static = models.BooleanField(default=False)
# Some other fields
class Sensor(models.Model):
device_id = models.CharField(max_length=100, primary_key=True)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT)
# Some other fields
class Asset(models.Model):
name = models.CharField(max_length=100)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT) # I need to use this field to filter below
sensor = models.ForeignKey(Sensor, on_delete=models.PROTECT, limit_choices_to={'sensor_type': WHAT DO I PUT HERE?},)
# Some other fields
I need to limit the choices in the sensor field of asset so that only sensors with the sensor_type set in the field immediately above, show up.
The reasoning behind this is that there will eventually be many sensors and it would be very useful to filter this. Initially I only need this to work from the admin page but this will eventually extend when I make my Create and Update Views.
Is this even possible? I'm essentially trying to access attributes before the object has actually been created.
After reading several other questions such as this one I have also looked into ModelChoiceField but the same issue exists of trying to access the form data before it has been submitted.
I'm very open to changing the model structure if that is what is required.

Django - ForeignKey to model with multiple choices

Sorry if the title is a bit unclear, I am a beginner and I have a few questions about database design.
I am building django app that would store info about a Book and another class Category that contains a list of genres. I would like to connect these tables so that one could choose category when inserting the book, however the categories are not showing up (I'm using django forms). Do you maybe know why?
CATEGORIES = (('Thriller', 'Thriller'), ...)
class Category(models.Model):
name = models.CharField(max_length=40, choices=CATEGORIES, default='Unknown')
class Book(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=200)
.......
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Or should I just include category inside the Book class and not connect them with ForeignKey? What would be the pros and cons of this? I already changed my DB few times splitting things up and again just putting it back to Book class as I don't really know what is better design in this situation.
Thanks

Listing all movies of a specific cinema - Foreign Key Django

I am new to Django and I have a question regarding the models and foreignkeys.
I have two models: Cinema and Movie.
One Cinema can have multiple movies, so I placed the foreign key of cinema in the Movie model.
class Cinema(models.Model):
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
class Movie(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
posting_cinema = models.ForeignKey('cinemas.Cinema', on_delete=models.CASCADE, null=True)
Now I want to list all the movies of a specific Cinema.
How can I do it?
The idea is the following:
The user clicks on a cinema, it opens a page with the cinema details and a button "see movies". If the user clicks this button, a new page opens and I want to have listed there the movies of that specific cinema.
I tried to figured out some solutions but sadly I am stuck. I was thinking about Movie.objects.filter(#something) but I am not sure
For a given Cinema you can use:
some_cinema.movie_set.all()
Or you can give the ForeignKey a more sensical name to query in reverse:
class Movie(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
posting_cinema = models.ForeignKey(
'cinemas.Cinema',
on_delete=models.CASCADE,
null=True,
# here we give the reverse relation a name
related_name='movies'
)
then you can query this with:
some_cinema.movies.all()
An alternative is to filter based on the ForeignKey, for example:
Movie.objects.filter(posting_cinema=some_cinema)
or if you have the primary key of the cinema:
Movie.objects.filter(posting_cinema__pk=some_cinema_pk)
(this can for example save a fetch of the Cinema object, if you never really need the Cinema itself).

Django: add choices to a ForeignKey field

I'm trying to add a choices option to a ForeignKey field. But that prevents the input form from getting validated.
I'm not sure if you can use choices in ForeignKey field, but how else would you provide human readable output for the choices?
I don't want the user to be able to choose from all groups and don't want to display the actual group name as it contains additional info for the admins, that shouldn't be visible to the user, for cosmetic reasons.
Any idea how to achieve that would be greatly appreciated.
Here is my code:
The model:
from django.contrib.auth.models import Group as DjangoGroup
class Category(models.Model):
created_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='category_author')
last_edited_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='category_editor')
name = models.CharField(max_length = 100)
privileges = models.ForeignKey('auth.Group', on_delete=models.PROTECT, blank=True, null=True, choices=((DjangoGroup.objects.get(pk=5), 'Test group'),))
The Form:
from django import forms
from .models import *
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
exclude = ('created_by', 'last_edited_by',)
With this code the form gives the desired option 'Test group', but when I try to submit it I get the following error:
Select a valid choice. TestGroup(just for testing purposes) is not one of the available choices.
with 'TestGroup(just for testing purposes)' being the actual group name that I am trying to hide from the user.
I hope you can understand what I am trying to achieve. Am I on the right track or is there a different way to do this?
Thanks in advance for any answer

django ModelForm foreign key - text widget add if not exist

I am just starting out with Django and have the following:
models.py:
class Song(models.Model):
name = models.CharField(max_length=200, blank=False, null=False)
artist = models.ForeignKey(Artist, blank=False, null=False)
class Artist(models.Model):
name = models.CharField(max_length=200, unique=True)
Now I have a model form for Song but currently have no ability to add artists that don't already exist (rendered as a dropdown). It would be nice to allow users to add artists on the fly but haven't been able to find a way to get that working. I saw lots of answers relating to replicating the admin "add another..." but kept running into roadblocks and outdated information.
What I tried:
replicating the "add another" from the admin console
Creating as a regular form - but abandoned it because ModelForms gives me a lot for free
Started researching on formsets but got stuck there, could not find a working example
Is there a way to easily add another artist from the song form? I don't mind rendering a new textbox below the artist selection where the user can add a new artist but I don't know how to do that with ModelForms and then add the artist into the database before saving.
Any advice would be much appreciated!
It would help to see what you are using to create your forms. I assume you are using a ModelFrom. If you are using jQuery I think you could use the following in your forms.py to capture a new artist. However, if you are using jQuery I would save individual forms as templates and display them as necessary depending on a button or link event for a new artist.
forms.py
class SongForm (forms.ModelForm):
new_artist_name = forms.CharField()
class Meta:
model = Song
def save(self, commit=True):
# do something with self.cleaned_data['new_artist']
new_artist = Artists.objects.filter('new_artist_name')
if new_artist.exists():
# Save song to artist.
else:
# Create and save new artist and save song to the
# new artist.
return super(SongForm, self).save(commit=commit)

Categories