Django forms and querysets - django

Lets say I need to filter the options available in a multiple select box.
in my view I have:
class ArticleCheckbox(forms.ModelForm):
article= forms.ModelMultipleChoiceField(queryset=Article.objects.all(),required=False, widget=forms.CheckboxSelectMultiple)
class Meta:
model = Book
fields = ('m2m_article',)
.
In my view I will assign:
articleform = ArticleCheckbox()
articleform.fields["m2m_article"].queryset = Article.objects.filter(category = "Animals")
How does the assigning of the queryset in the view affect the queryset from classes (Article.object.all()) ?
Does it overwrite? I do not think so.
I would like to override the queryset. How can I do it?

Does this work?
article=forms.ModelMultipleChoiceField(queryset=Article.objects.all().filter(category = "Animals"),required=False, widget=forms.CheckboxSelectMultiple)
Directly in the model. Or do you want to leave the filtering to the view to do different things?

The way you're doing it is correct, except for you assign the class and not an instance of ArticleCheckBox
articleform = ArticleCheckbox()
When the form is initialised it is given a default queryset and you are overriding it, the initial one will never query the database since no data is ever needed to be retrieved at that point.

Related

How to populate django form with selection from your models

I have a CreateSong CBV in Django that allows me to create song objects to a model. My question is, in the form I created for the view, how do I make the album column to be auto-populated with albums the user-created only? I get errors calling "self" that way.
See my views below
class CreateSong(CreateView):
model = Song
fields = [album, song_title]
fields['album'].queryset = Album.objects.filter(owner=self.request.user)
I think you should override get_form. See the example below:
class CreateSong(CreateView):
model = Song
fields = [album, song_title]
def get_form(self):
form = super().get_form()
form.fields['album'].queryset = Album.objects.filter(owner=self.request.user)
return form
You do not have access to self.request.user, because you are calling it at class level, thus when the class is being defined and not when the view is actually called. Instead you should override the get_form method as in Davit's answer.

Django - force pk_url_kwarg to query other model instances

Consider the following code:
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Is there any way that I can force pk_url_kwarg to query
& return other model instances defined in models.py (like model = other_model), while having my get_object() method to return objects in model = BHA_overall? What CBV should I use (I think UpdateView is not a good choice in this case)?
++ I'm trying to make a page that allows users to manage information about the product they use. So, ultimately I will implement forms, and the user input needs to be saved in DB
++ I need pk_url_kwarg = 'pk_alt' to query other models and generate url. But I still need get_object() method to return objects in model = BHA_overall to generate form fields on the user side.
From my understanding you need a django form generated from BHA_overall, but the data should be saved to AnotherModel right?
I will propose 2 solutions to this problem, Choose what best fits you.
Multiple views:
Have multiple views for the task, What I mean is create a view which creates the form for the frontend using BHA_overall, you can create both Create and Update view this way and update view's initial could be overwritten so form will have expected value when editing. And now post the data to another view which handles the post data. This view can have your AnotherModel doing its thing.
Using Django Form:
If you dont like having multiple views, You can keep things simple by creating a form yourself. Create a DjangoForm with the same fields you want to show to the user and use it in to create your own views, Now you wont need BHA_overall and use your AnotherModel to save datal.

How to get related objects from one to many relationship for display in ListView and be able to filter?

I'm looking at this tutorial from the Mozilla library. I want to create a list view in admin based on a database relationship. For example I have a Vehicle model and a statusUpdate model. Vehicle is a single instance with many statusUpdates. What I want to do is select the most recent statusUpdate (based on the dateTime field I have created) and have that data available to me in the list view.
The tutorial mentions:
class Vehicle(models.Model):
class statusUpdate(models.Model):
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
Question: How could I do a list view with model relationships and be able to filter by fields on the child relationship and pass to the view?
Here's what I wanted in a Class Based View (CBV), my explanation of my issue was not very clear.
def get_context_data(self, **kwargs):
get_context_data is a way to get data that is not normally apart of a generic view. Vehicle is already provided to the View because its the model defined for it, if you wanted to pass objects from a different model you would need to provide a new context, get_context_data is the way to do this. statusUpdate is a model with a foreign key to Vehicle. Full example below.
class VehicleDetail(generic.DetailView):
model = Vehicle
template_name = 'fleetdb/detail.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(VehicleDetail, self).get_context_data(**kwargs)
context['updates'] = statusUpdate.objects.filter(vehicle_id=1).order_by('-dateTime')[:5]
return context
I don't think that solves your problem entirely. You used this:
context['updates'] = statusUpdate.objects.filter(vehicle_id=1).order_by('-dateTime')[:5]
This will only result in a list of statusUpdates where vehicle_id is set to 1. The part I was struggling with is how to get the primary key (in your case the actual vehicle_id). I found this solution:
vehicle_id = context['vehicle'].pk # <- this is the important part
context['updates'] = statusUpdate.objects.filter(vehicle_id=vehicle_id).order_by('-dateTime')[:5]
I discovered the context object and it contains the data which has already been added (thus you need to call super before using it). Now that I write it down it seems so obvious, but it took me hours to realize.
Btw. I am pretty new to Django and Python, so this might be obvious to others but it wasn't to me.

Class Based Views UpdateView django different type of model

how can I change model in UpdateView for different type of users? I have Student and Teacher inherited from AbstractBaseUser, and I need edit for for them
class EditUser(UpdateView):
success_url = '/success/'
template_name = 'edit-profile.html'
model = Teacher (I need to choose this Teacher or Student)
I know about get_template_names(self) method or get_success_url(self), but can not find any get_model method.
I need somethng like:
def get_model_name(self):
if self.request.user.user_type == 'teacher':
return Teacher
if self.request.user.user_type == 'student':
return Studend
Thank you.
According to Django documentation:
model
The model that this view will display data for. Specifying model = Foo
is effectively the same as specifying queryset = Foo.objects.all(),
where objects stands for Foo’s default manager.
queryset
A QuerySet that represents the objects. If provided, the value of
queryset supersedes the value provided for model.
get_queryset()
Returns the queryset that will be used to retrieve the object that
this view will display. By default, get_queryset() returns the value
of the queryset attribute if it is set, otherwise it constructs a
QuerySet by calling the all() method on the model attribute’s default
manager.
So, all you need is redefine get_queryset method

Django restrict options of ManyToMany field in ModelForm based on model instance

I want to show only options already stored in models' ManyToManyField.
I have model Order which I want to have a Model based form like this:
class OrderForm(ModelForm):
class Meta:
model = Order
fields = ['amount', 'color']
Now I do not want to display all colors as choices, but instead only color instances saved in ManyToManyField of another model. The other model is Design:
class Design(models.Model):
color = models.ManyToManyField('maker.Color')
# ...
Is this at all possible while using ModelForm?
Attempt
I have tried doing it by having a ModelForm of Design and setting instance:
class ColorForm(ModelForm):
class Meta:
model = Design
fields = ['color']
And then in view:
color_form = ColorForm(instance=design)
But I don't exactly understand what setting instance does, and I think instance is not what I am looking for as it still lists all colors.
The instance setting has nothing to do with limiting the choices. In essence, it simply populates the form's values with the ones from a specific record. You usually provide an instance in an edit operation, whereas you skip it in an add operation.
The representation of a models.ManyToManyField in the ModelForm is a forms.ChoiceField for which you can simply override its queryset property, and specify the queryset you desire.
Therefore, in your view:
form = OrderForm()
form.fields['color'].queryset = Design.object.all() # for example