ModelChoiceField lists tuples instead of simple values - django

I have a django form with a ModelChoiceField input and it lists the result as tuples instead of simple values.
I have no clue on how to do it.
DJANGO
class PayThis(forms.Form):
amount = forms.FloatField(required=False)
cost2 = forms.FloatField(required=False)
year = forms.ModelChoiceField(required=False,queryset=AnAgricol.objects.values_list('anul').all())
HTML
<option value="(2019,)">(2019,)</option>
I expect to get this: < option value="2019">2019< /option >

A ModelChoiceField deals, like the name suggests, with model objects. You thus can not use values_list here.
Your Form thus should define something like:
class PayThis(forms.Form):
amount = forms.FloatField(required=False)
cost2 = forms.FloatField(required=False)
year = forms.ModelChoiceField(required=False, queryset=AnAgricol.objects.all())
You can override the __str__ to return a string representation of the year, like:
AnAgricol(models.Model):
anul = models.IntegerField()
def __str__(self):
return str(self.anul)
If the year however is not something to represent AnAgricol, then you could use a ChoiceField [Django-doc], like:
class PayThis(forms.Form):
amount = forms.FloatField(required=False)
cost2 = forms.FloatField(required=False)
year = forms.ChoiceField(required=False, choices=[])
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['year'].choices = [
(yr, yr) for yr
in AnAgricol.objects.values_list('anul', flat=True).distinct()
]

Related

Queryset takes too long to populate data

I have a django view that generates a form depending on POST or GET. It works well with little data on the Student model but stagnates when the data gets big. Is there a remedy I can apply??
Here's the view
def My_View(request,pk):
if request.method == 'POST':
try:
form = NewIssueForm(request.POST,school= request.user.school,pk=pk,issuer = request.user)
if form.is_valid():
name = form.cleaned_data['borrower_id'].id
form.save(commit=True)
books = Books.objects.get(id=pk)
Books.Claimbook(books)
return redirect('view_books')
messages.success(request,f'Issued successfully')
except Exception as e:
messages.warning(request,f"{e}")
return redirect('view_books')
else:
form = NewIssueForm(school= request.user.school,pk=pk,issuer = request.user)
return render(request, 'new_issue.html', {'form': form})
Here's the form
class NewIssueForm(forms.ModelForm):
def __init__(self,*args, pk,school,issuer, **kwargs):
super(NewIssueForm, self).__init__(*args, **kwargs)
booqs = Books.objects.filter(school=school).get(id=pk)
self.fields['issuer'].initial = issuer
self.fields['borrower_id'].Student.objects.filter(school = school)
self.fields['book_id'].label = str(booqs.book_name) + " - " + str(booqs.reg_no)
self.fields['book_id'].initial = pk
class Meta:
model = Issue
fields = ['issuer','book_id','borrower_id','due_days']
widgets = {
'book_id':forms.TextInput(attrs={"class":'form-control','type':''}),
'issuer':forms.TextInput(attrs={"class":'form-control','type':'hidden'}),
'borrower_id': Select2Widget(attrs={'data-placeholder': 'Select Student','style':'width:100%','class':'form-control'}),
'due_days':forms.TextInput(attrs={"class":"form-control"}),
Student model
class Student(models.Model):
school = models.ForeignKey(School, on_delete=models.CASCADE)
name = models.CharField(max_length=200,help_text="The student's name in full")
now = datetime.datetime.now()
YEAR = [(str(a), str(a)) for a in range(now.year-5, now.year+1)]
year = models.CharField(max_length=4, choices = YEAR,help_text='The year the student is admitted in school')
student_id = models.CharField(max_length=40,help_text = "This is the student's admission number")
klass = models.ForeignKey(Klass,on_delete=models.CASCADE)
stream = models.ForeignKey(Stream,on_delete=models.CASCADE)
graduated = models.BooleanField(default=False,help_text = "Tick the box to mark the student as graduated")
prefect = models.BooleanField(default=False,help_text = "Tick the box to select the student as a prefect")
def __str__(self):
return "%s - %s - F%s %s"%(self.student_id,self.name,self.klass,self.stream)
class Meta:
verbose_name_plural = 'Students'
unique_together = ("school", "student_id",)
indexes = [models.Index(fields=['student_id']),
models.Index(fields=['name', ]),
The reason this happens is because when you render the Student, it will need to fetch the related Klass and Stream objects, and will make a query per Student object. This problem is called an N+1 problem since fetching n student requires one query to fetch all the Students, and then one query per Student for the Klasses, and one query per Student for the Streams.
You can select the data when you select the students with:
self.fields['borrower_id'].Student.objects.filter(
school=school
).select_related('klass', 'stream')
Depending on the __str__ implementations of other models, you might have to select data along with these models as well.
Can't guess your model but it seems like select_related may play a trick.

Dynamic queryset filtering with multiple query parameters

I have the following view I have implemented to filter transactions depending on a certain query provided. Is there a way I could filter dynamically depending on what query is provided, for example one would want to only query year and month only or even filter using one query. The rest of the query values will end up having a None value an should not be included in the filter.
class ReportTransactionsFilterAPIView(APIView):
def get(self, request, format=None):
year = self.request.GET.get('year')
month = self.request.GET.get('month')
subcounty = self.request.GET.get('subcounty')
ward = self.request.GET.get('ward')
fromDate = self.request.GET.get('fromDate')
toDate = self.request.GET.get('toDate')
qs = LiquorReceiptTrack.objects.all()
qs = LiquorReceiptTrack.objects.filter(
Q(date_recieved__range=[fromDate,toDate])|
Q(date_recieved__year=year)|
Q(date_recieved__month=month)|
Q(sub_county__iexact=subcounty)|
Q(ward__iexact=ward)
)
You can use django-filter for your solution. Also for more information you can visit https://www.django-rest-framework.org/api-guide/filtering/
Just use if, why Q do you use here?:
year = self.request.GET.get('year')
month = self.request.GET.get('month')
subcounty = self.request.GET.get('subcounty')
ward = self.request.GET.get('ward')
fromDate = self.request.GET.get('fromDate')
toDate = self.request.GET.get('toDate')
qs = LiquorReceiptTrack.objects.all()
if year:
qs = qs.filter(date_recieved__year=year)
if month:
qs = qs.filter(date_recieved__month=month)
...
Went over the django-filter documentation and refactored the list view:
class TransactionFilter(django_filters.FilterSet):
date_range = DateFromToRangeFilter()
month = django_filters.NumberFilter(field_name='date_recieved', lookup_expr='month')
year = django_filters.NumberFilter(field_name='date_recieved', lookup_expr='year')
date = django_filters.DateFilter(field_name='date_recieved', lookup_expr='exact')
sub_county = django_filters.CharFilter(lookup_expr='icontains')
ward = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = LiquorReceiptTrack
fields = ['date','month','year','date_range','sub_county','ward']
class ReportTransactionsFilterAPIView(generics.ListAPIView):
queryset = LiquorReceiptTrack.objects.all()
serializer_class = ReceiptDetailSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = TransactionFilter

Render django.forms.fields.ChoiceField object

In my Django Project I have the following Problem:
I would like to have a dynamic Django form. In the first step the user is asked something by the first form. When I get the postmethod the variables should be used for genereating a new form
my views.py
def calc(request):
if request.method =="POST":
get_form = CalculationForm(request.POST)
if get_form.is_valid():
op = get_form.cleaned_data['op']
ab = get_form.cleaned_data['ab']
alternative = AlternativForm(optype = op, wsgroup = ab)
return render(request, 'calculated_lensar.html', {"alternativ" : alternativ})
else:
form = CalculationForm()
return render(request, 'calc.html', {'form': form})
The secondform (postmethod) looks like
class AlternativForm(forms.Form):
praep_button = ((3, 'hallo'), (4, 'tschüss'))
def __init__(self, optype, wsgroup, *args, **kwargs):
super(AlternativForm, self).__init__(*args, **kwargs) #dont know for what this is standing
self.optype = optype
self.wsgroup = wsgroup
self.values = self.read_db()
self.praep_button = self.buttons()
self.felder = self.blub()
self.neu2 = self.myfield_choices()
def read_db(self):
import sqlite3
....
return result #tuple with 15x5 elements
def buttons(self):
praep_button = []
for i in self.values:
praep_button.append((i[4], i[1]))
return praep_button #Just formating result from read_db in tuple(15x2)
def blub(self):
return forms.ChoiceField(widget=forms.RadioSelect, choices=self.praep_button)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button) #print --><django.forms.fields.ChoiceField object at 0x751f9b90>
def myfield_choices(self):
field = self['myfield']
"""i think here is the problem.
Above 'myfield' is a django.forms.fields.ChoiceField object, but here it is rendered to html (like it should be). I have the code from https://stackoverflow.com/questions/6766994/in-a-django-form-how-do-i-render-a-radio-button-so-that-the-choices-are-separat.
But instead i should use field = self.felder (radioselect woth tuple of the db)"""
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.render(name, field.value(), attrs=attrs)
#return widget.get_renderer(name, field.value(), attrs=attrs)
So all in all I hope the problem is clear.
If i am using AlternativForm() i get the constant form. Instead i would like to get a dynamic form. If I access in views.py:
alternative = AlternativForm(optype = op, wsgroup = ab)
alternative = alternativ.felder
than I get . Can I render that to html?
If I set in forms.py:
field = self.felder
than I get the error that it is a field and not a widget
Thank you for reading!
You just need to assign the choices in the form's __init__() method. Almost what you're doing, but instead of defining self.felder to be a field, you need to use the already initialised form's fields:
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button)
def __init__(self, optype, wsgroup, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myfield'].choices = self.get_choices(optype, wsgroup) # create your choices in this method
def get_choices(optype, wsgroup):
# call your other methods here
return praep_button

django - how to add additional parameters to queryset in forms

i want to add additional parameters to the queryset in forms
class Reserve(forms.ModelForm):
food_name = forms.ModelChoiceField(queryset=Food.objects.all())
def __init__(self, year=None, month=None, day=None, *args, **kwargs):
super(Reserve, self).__init__(*args, **kwargs)
self.year = kwargs.pop('year')
self.month = kwargs.pop('month')
self.day = kwargs.pop('day')
self.fields['food_name'].queryset = Food.objects.filter(
serve_date__year = year, serve_date__month = month, serve_date__day = day)
class Meta:
model = Reservation
fields = ('food_count', 'food_name')
but its return KeyError:
'year'
Year is an explicit keyword parameter here, it isn't in kwargs.
self.year = year
And so on for the other parameters you've listed explicitly.

how to automatically find the sum of few fields and save it in another field in DJANGO?

class EmployeeRating(models.Model):
rating_1 = models.IntegerField(default = 0)
rating_2 = models.IntegerField(default = 0)
rating_3 = models.IntegerField(default = 0)
rating_4 = models.IntegerField(default = 0)
rating_4 = models.IntegerField(default = 0)
total = models.IntegerField(default = 0)
Using a Model Form to take the values, automatically assign values to 'total'.
1. Don't repeat yourself
It seems like a bad idea to have a field in your model containing the total of some other fields in the same model. It would be all too easy to update one and not the other, leaving the fields inconsistent.
So my suggestion is to drop the total field and compute it when you need it.
You can add a method to the EmployeeRating model that computes it in Python:
class EmployeeRating(models.Model):
# ...
#property
def total_rating(self):
return self.rating_1 + self.rating_2 + self.rating_3 + self.rating_4
And if you need to query on the total, you could use extra():
EmployeeRating.objects.extra(
where = ['rating_1 + rating_2 + rating_3 + rating_4 > 10'])
2. Normalize!
The reason why this is awkward is that your model is not fully normalized. If you have multiple ratings attached to an EmployeeRating record, the natural way to implement them is to have a separate table. If you did this, your model would look like this:
class EmployeeRating(models.Model):
# ... other fields, e.g. foreign key to Employee model.
class Rating(models.Model):
employee_rating = models.ForeignKey(EmployeeRating, related_name='ratings')
rating = models.IntegerField()
# ... other fields, e.g. who performed the evaluation, or when it was done.
and then you can use annotate() when you need to get the total:
from django.db.models import Sum
EmployeeRating.objects.annotate(total_rating = Sum('ratings__rating'))
class EmployeeRatingForm(forms.ModelForm):
class Meta:
model = EmployeeRating
def __init__(self, *args, **kwargs):
super(EmployeeRatingForm, self).__init__(*args, **kwargs)
self.fields['total'].widget = forms.HiddenInput()
def save(self, commit=True):
rating = super(EmployeeRatingForm, self).save(commit=False)
rating1 = self.cleaned_data['rating_1']
rating2 = self.cleaned_data['rating_2']
rating3 = self.cleaned_data['rating_3']
rating4 = self.cleaned_data['rating_4']
rating.total = rating1 + rating2 + rating3 + rating4
rating.save()