How to generate a dropdown input with a Django ModelForm? - django

I have the following model:
from django.db import model
class Fruit(models.Model):
fruit_name = models.CharField(max_length=100)
And A form like this:
from django import forms
from .models import SomeModel
class FruitForm(forms.ModelForm):
some_input = forms.CharField(max_length=100)
class Meta:
model = Fruit
fields = ['fruit_name']
This guide shows me how to create a dropdown like this:
fruit = [
('orange', 'Oranges'),
('cantaloupe', 'Cantaloupes'),
('mango', 'Mangoes'),
('honeydew', 'Honeydews'),
]
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.CharField(widget=forms.Select(choices=FRUIT_CHOICES))
What I want to happen is for the key/value tuple list to generate based on the fruit_id and fruit_name columns of the Fruit model, without the need to manually insert the data into the FruitForm.
What am I missing here?

Usually you do that with a ModelChoiceField [Django-doc]:
from django import forms
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.ModelChoiceField(queryset=Fruit.objects.all())
In your Fruit model, you then implement the __str__ method to decide how to render your fruit. Here that would be:
from django.db import model
class Fruit(models.Model):
fruit_name = models.CharField(max_length=100)
def __str__(self):
return self.fruit_name
You can alter the queryset= parameter, for example to filter the queryset in advance. For example if you only want to show Fruit that starts with an A, you can filter like:
from django import forms
# example: only show fruit that starts with an A
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.ModelChoiceField(
queryset=Fruit.objects.filter(fruit_name__startswith='A')
)

Related

How to use django-filter with django-select2?

I want to implement a field with autocomplete input (django-select2) in the Filterset form (django-filter):
import django_filters
from django_select2.forms import Select2Widget
from .models import Product
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = ['product','manufacturer']
widgets = {'product':Select2Widget()}
or this:
class ProductFilter(django_filters.FilterSet):
product = django_filters.ModelChoiceFilter(
queryset=Product.objects.all(),
widget=Select2Widget)
class Meta:
model = Product
fields = ['product','manufacturer']
These ways don't work. Any help?
I didnt find this in the docs. But you must include include the form media in your template: {{ filter.form.media }}.

Applying a Django FilterSet to an Annotated QuerySet?

I'm trying to apply a filter to an annotated queryset, like is done here:
https://docs.djangoproject.com/en/2.0/topics/db/aggregation/#filtering-on-annotations
Similar to the snippet below, I'd like to make that highly_rated piece a FilterSet that picks up the request parameters
highly_rated = Count('books', filter=Q(books__rating__gte=7))
Author.objects.annotate(num_books=Count('books'), highly_rated_books=highly_rated)
I'd like to make that highly_rated piece a FilterSet that picks up the request queries. Something like:
class MainFilter(FilterSet):
class Meta:
model = Author
fields = {
author: ['in', 'exact'],
genre: ['in', 'exact']
}
class AnnotatedFilter(FilterSet):
class Meta:
fields = {
'books__rating': ['gt', 'lt' ],
}
class MyView(ListApiView):
filter_class = AuthorFilter
def get_queryset(self):
annotated_filter = AnnotatedFilter(request) # how do I set up another filter based on the request?
Author.objects.annotate(num_books=Count('books'), FilterSet=annotated_filter) # how do I apply it to an annotated queryset?
All so I can query something like:
api/authors/?genre=Fantasy&books__rating__gt=5
Where one of the parameters executes against all the records and other params execute against the annotated part.
Any help is greatly appreciated. Thanks!
Change get_queryset() method to
def get_queryset(self):
return Author.objects.annotate(num_books=Count('books')
and define that field in Filter class as
class AuthorFilter(FilterSet):
num_books = filters.NumberFilter(name='num_books')
class Meta:
model = Author
fields = ['num_books',]#other fields
then use the api as /api/end/point/authorlist/?num_books=10
EXAMPLE
models.py
from django.db import models
class Sample(models.Model):
foo = models.IntegerField()
bar = models.IntegerField()
views.py
from django.db.models import F
from django_filters import rest_framework as filters
class MySampleAPI(viewsets.ModelViewSet):
filter_backends = (filters.DjangoFilterBackend,)
filter_class = SampleFilterClass
serializer_class = SampleSerializerClass
def get_queryset(self):
return Sample.objects.annotate(foo_bar_sum=F('foo') + F('bar'))
filters.py
from django_filters import rest_framework as filters
class SampleFilterClass(filters.FilterSet):
sum = filters.NumberFilter(name='foo_bar_sum', lookup_expr='gte')
class Meta:
model = Sample
fields = ['sum', ]
JPG's answer worked for me, but I had to replace name by label in the Filter Class:
class AuthorFilter(FilterSet):
num_books = filters.NumberFilter(label='num_books') # here
class Meta:
model = Author
fields = ['num_books',]#other fields

How to use a choiceField declared in the model, in a form. django

I have this in my model.py
class marca(models.Model):
marcas = (
('chevrolet', 'Chevrolet'),
('mazda', 'Mazda'),
('nissan', 'Nissan'),
('toyota', 'Toyota'),
('mitsubishi', 'Mitsubishi'),
)
marca = models.CharField(max_length=2, choices= marcas)
def __unicode__(self):
return self.marca
And I need to use it in my form.py
I tried this but it doesn't work.
class addVehiculoForm(forms.Form):
placa = forms.CharField(widget = forms.TextInput())
tipo = forms.CharField(max_length=2, widget=forms.Select(choices= tipos_vehiculo))
marca = forms.CharField(max_length=2, widget=forms.Select(choices= marcas))
Move your choices to be above the model, in the root of your models.py:
marcas = (
('chevrolet', 'Chevrolet'),
('mazda', 'Mazda'),
('nissan', 'Nissan'),
('toyota', 'Toyota'),
('mitsubishi', 'Mitsubishi'),)
class Marca(models.Model):
marca = models.CharField(max_length=25,choices=marcas)
Then in your file where you declare the form:
from yourapp.models import marcas
class VehiculoForm(forms.Form):
marca = forms.ChoiceField(choices=marcas)
I also fixed some other problems for you:
Class names should start with a capital letter
You need to increase the max_length of your character field because you are storing the word chevrolet anytime someone will select Chevrolet in the choice drop down.
If you are just creating a form to save records for Marca model, use a ModelForm, like this:
from yourapp.models import Marca
class VehiculoForm(forms.ModelForm):
class Meta:
model = Marca
Now, django will render the choice field automatically.
You need to define the choices tuple marcas outside of model class class marca.
Then you can do following in forms.py to use
from models import marcas
class addVehiculoForm(forms.Form):
marca = forms.CharField(max_length=2, widget=forms.Select(choices= marcas))
...
There is also another way of doing this if the choices (tuple) is initialized as private attribute like so
models.py
class Marca(models.Model):
__marcas = ( #private attributes
('chevrolet', 'Chevrolet'),
('mazda', 'Mazda'),
('nissan', 'Nissan'),
('toyota', 'Toyota'),
('mitsubishi', 'Mitsubishi'),
)
marca = models.CharField(max_length=100, choices= __marcas)
def __unicode__(self):
return self.marca
forms.py
from yourapp.models import Marca #import model instead of constant
class VehiculoForm(forms.Form):
marca = forms.ChoiceField(choices= Marca._meta.get_field('marca').choices)
#If you want to get the default model field
#or just straight get the base field (not prefer solution)
marca = Marca._meta.get_field('marca')
#or define in class Meta (prefer and better solution)
class Meta:
model = Marca
fields = ('marca',)
I prefer holding choices still inside the model, so they do not get mixed with other models. Then the solution would be:
models.py:
Same as in question.
forms.py:
from yourapp.models import marca
class VehiculoForm(forms.Form):
marca = forms.ChoiceField(choices=marca.marcas)
class Meta:
model = marca
fields= ('marca')
You could also just define meta, if you want default model fields.

django foreign key

I have the following in my models.py:
from django.db import models
class LabName(models.Model):
labsname=models.CharField(max_length=30)
def __unicode__(self):
return self.labsname
class ComponentDescription(models.Model):
lab_Title=models.ForeignKey('Labname')
component_Name = models.CharField(max_length=30)
description = models.CharField(max_length=20)
purchased_Date = models.DateField()
status = models.CharField(max_length=30)
to_Do = models.CharField(max_length=30,blank=True)
remarks = models.CharField(max_length=30)
def __unicode__(self):
return self.component
I have the following in my admin.py:
from django.contrib import admin
from Lab_inventory.models import ComponentDescription,LabName
class ComponentDescriptionAdmin(admin.ModelAdmin):
list_display= ('lab_Title','component_Name','description','purchased_Date','status','to_Do','remarks')
list_filter=('lab_Title','status','purchased_Date')
admin.site.register(LabName)
admin.site.register(ComponentDescription,ComponentDescriptionAdmin)
What I want is to display the fields under the component description to be displayed under the lab title(the fields related to each lab title by should be displayed under that lab name)
What you are doing with list_display and list_filter pertain to the list that is shown in the admin screen where the list of LabName objects are listed.
Assuming one LabName has one-to-many ComponentDescription entities, you need Django's InlineModelAdmin to display the list of ComponentDescription objects belonging to LabName within the admin page for a specific LabName entity. The code would be of the following structure:
from django.contrib import admin
from Lab_inventory.models import ComponentDescription,LabName
class ComponentDescriptionInline(admin.TabularInline):
model = ComponentDescription
class LabNameAdmin(admin.ModelAdmin):
inlines = [
ComponentDescriptionInline,
]
admin.site.register(LabName, LabNameAdmin)
where TabularInline is a subclass of the generic InlineModelAdmin.

django ForeignKey model filter in admin-area?

Hi I need really very very simple example. First my models:
#This my student models
from django.db import models
SEX_CHOICES= (
('M', 'Male'),
('F', 'Female'),
)
class Students(models.Model):
student_name = models.CharField(max_length=50)
student_sex = models.CharField(max_length=8, choices=SEX_CHOICES)
student_city = models.Charfield(max_length=50)
student_bio = models.TextField()
def __unicode__(self):
return self.student_name
O.K. Let see my ClassRooms Model.
#This my ClassRooms models
from django.db import models
from myproject.students.models import *
class ClassRooms(models.Model):
class_number= models.CharField(max_length=50)
class_student_cities = models.ForeignKey(Students)
class_year = models.DateField()
def __unicode__(self):
return self.class_number
How can i show in the class_student_cities area the Students.student_city datas? I guess that about django-admin area. When i do it withclass_student_cities = models.ForeignKey(Students) i just see in that area the Students.student_name data (ex: John Smith). I want to see JUST Students.student_cities data (ex: NewYork). Can you give me a little example?
Should i use something like that:
class_student_cities = models.ForeignKey(Students.student_cities)
Many Thanks!
Try redifinition unicode method.
def __unicode__(self):
return self.student_city
So you'll see in the field student city.
Well, I tried to remake your application to set data with forms class. Something like this in admin.py in your application:
from django.contrib import admin
from django import forms
from myapp.models import *
class ClassRoomsAdminForm(forms.ModelForm):
class Meta:
model = ClassRoom
def __init__(self, *arg, **kwargs):
super(ClassRoomsAdminForm, self).__init__(*arg, **kwargs)
self.fields[' class_student_cities'].choices = [(csc.id,csc.student_city) for csc in Students.objects.all()
class ClassRoomsAdmin(admin.ModelAdmin):
form = ClassRoomsAdminForm
admin.site.register(ClassRooms,ClassRoomsAdmin)
Maybe you'll need to fix something, but I hope it will work. You will set init function to your forms, so in admin panel you set all choices to everything you keep in your Students model. csc.id you'll need to make this object iterable (cities aren't unique) and then you can choose everything from Students model to set in the field.