What is the easy way to create django form - django

For example I have got the Student model
class Student(models.Model):
first_name = models.CharField(max_lenght=100)
last_name = models.CharField(max_lenght=100)
learning_group = models.CharField(max_lenght=100)
So I want to create the following form for each student in some group:
| Student | Mark | Absent |
| John Smyth | input for mark | input for absent hours|
Thank you for your help.
EDIT:
I am storing my absent and mark fields in JSonField() and it look like this
{ student_pk: 1, mark: 5, absent: 3 }
So what did I tried:
At first I tried to implement this through overriding forms.Form init method but it was formatted wrong
def MarkBookForm(forms.Form):
def __init__(self, *args, **kwargs):
self.students = kwargs.pop('students', None)
super(MarkBookForm, self).__init__(*args, **kwargs)
for student in self.students:
self.fields["student_{}".format(student.pk)] = forms.CharField(initial="{} {}".format(student.first_name, student.last_name)
self.fields["mark_{}".format(student.pk)] = forms.FloatField()
self.fields["absent_{}".format(student.pk)] = forms.FloatField()
This is what I want
And this what i did

First of all you should define your model for mark and absent
You can use sth like this( not tested :) ):
models.py:
class MyModel(models.Model):
field1 = models.CharField(max_length=40, blank=False, null=False)
field2 = models.CharField(max_length=60, blank=True, null=True)
forms.py:
class FormForMyModel(forms.Form):
form_field1 = forms.CharField(max_length=40, required=True)
form_field2 = forms.CharField(max_length=60, required=False)
views.py:
def create_a_my_model(request):
if request.method == 'POST':
form = FormForMyModel(request.POST)
if form.is_valid():
my_model = MyModel()
my_model.field1 = form.cleaned_data.get('form_field1', 'default1')
my_model.field2 = form.cleaned_data.get('form_field2', 'default2')
my_model.save()
else:
form = FormForMyModel()
context_data = {'form': form}
return HttpResponse('templtate.html', context_data)
The above example outlines generic form workflow.
You can notice two annoying issues in the above example:
I have to define Fields on MyModel and Fields on FormForMyModel separately. However, there is a lot of similarity between those two groups (types) of Fields, so that's kind of duplicate work. The similarity grows when adding labels, validators, etc.
creating of MyModel instance is a bit silly, having to assign all those values manually.
This is where a ModelForm comes in.
So back to the two issues:
Instead of defining a form Field for each model Field, I simply define model = MyModel in the the Meta class. This instructs the Form to automatically generate form Fields from model Fields.
Model forms have save method available. This can be used to create instance of model in one line in the view, instead of manually assigning field-by-field.
So, lets make the example above with a ModelForm:
models.py:
class MyModel(models.Model):
field1 = models.CharField(max_length=40, blank=False, null=False)
field2 = models.CharField(max_length=60, blank=True, null=True)
forms.py:
class MyModelForm(forms.ModelForm): # extending ModelForm, not Form as before
class Meta:
model = MyModel
views.py:
def create_a_my_model(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
# save the model to database, directly from the form:
my_model = form.save() # reference to my_model is often not needed at all, a simple form.save() is ok
# alternatively:
# my_model = form.save(commit=False) # create model, but don't save to database
# my.model.something = whatever # if I need to do something before saving it
# my.model.save()
else:
form = MyModelForm()
context_data = {'form': form}
return HttpResponse('templtate.html', context_data)

Related

How to pass current logged user in a form class in django

I am trying to create a form where one field is a ModelChoicefield. Im trying to populate that field with objects from a different model. I have ran into a problem as i need to get the current logged user within the form to filter the queryset. Here are the 2 models
class UserExercises(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Muscle = models.ForeignKey(Muscle, on_delete=models.CASCADE)
class Exercise(models.Model):
exercise = models.ForeignKey(UserExercises, on_delete=models.CASCADE)
weight = models.DecimalField(max_digits=6, decimal_places=3)
reps = models.PositiveIntegerField(validators=[MaxValueValidator(100)])
difficulty = models.CharField(max_length=30)
date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
And here is my form
class AddExerciseForm(forms.Form):
exercise = forms.ModelChoiceField(queryset=UserExercises.objects.filter(user=1))
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']
As you can see i am currently hard coding a filter in the ModelChoiceField, but want to replace that with the current users Id. Is there anyway of Going about this. Im new to django so any help would be Appreciated.
My View
#login_required
def add_exercise_view(request):
if request.method == 'POST':
user_id = request.user.id
form = AddExerciseForm(user_id=user_id)
if form.is_valid():
form.save()
return redirect('myfit-home')
else:
form = AddExerciseForm()
return render(request, 'users/register.html', {'form': form})
Firstly, AddExerciseForm should extend forms.ModelForm.
To initialize form data based on some paramater, you can override __init_ method of ModelForm to update form fields (that field is exercise in this case) based on some argument/parameter (which is user_id in this case).
class AddExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id', None)
super(AddExerciseForm, self).__init__(*args, **kwargs)
if user_id is not None:
# update queryset for exercise field
self.fields['exercise'].queryset = UserExercises.objects.filter(user=user_id)
else:
# UserExercises.objects.none() will return an empty queryset
self.fields['exercise'].queryset = UserExercises.objects.none()
And pass the user_id while initializing the form in view:
if request.user.is_authenticated():
# get user id
user_id = request.user
form = AddExerciseForm(user_id=user_id)
override __init__ method of the Form, and pass the user as argument
def __init__(self,user,*args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
self.fields['exercise'].queryset=
UserExercises.objects.filter(user=self.user))
self.fields['exercise'].widget = forms.CheckboxSelectMultiple
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']

Django modelforms, foreignkey filter for data owned by logged in user [duplicate]

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .

set slug field form manually in views Django

I'm new in Django and I'm trying to pre fill one of the fields of my form with a slug.
I'm getting the slug from another model. I'm not using ForeignKey because that shows me a list with my objects and I want to save in the form the same slug that I'm using in the url.
Maybe I'm not thinking this right. What should I do?
Thank you!
This are my models:
from django.db import models
class Thing(models.Model):
name = models.CharField(max_length=255,)
rut = models.CharField(max_length=12, blank= True)
cel = models.CharField(max_length=12, blank= True)
slug = models.SlugField(unique=True)
class Control(models.Model):
id_p = models.SlugField()
pa = models.CharField(max_length=3,)
My forms
from django.forms import ModelForm
from collection.models import Thing, Control, Medicamento
class ThingForm(ModelForm):
class Meta:
model = Thing
fields = ('name', 'rut','cel','pet',)
class ControlForm(ModelForm):
class Meta:
model = Control
exclude = ['id_p']
This is what I'm doing in the views
def add_control(request, slug):
thing = Thing.objects.get(slug=slug)
form_class = ControlForm
form_class(initial={'id_p':thing})
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
form.save()
return redirect('thing_detail', slug=thing.slug)
else: form = form_class()
return render(request, 'things/control.html', {
'thing': thing,
'form': form,
})
So, I figure it out!
In views.py, after " if form.is_valid():"
I put this:
prev = form.save(commit=False)
prev.id_p = thing.slug
prev.save()
In that way I put the data in the excluded field before I commit the form.

Populate foreign key fields using data from form based on ModelForm

I have set up my models as shown below:
Models.py
class Customer(models.Model):
CustomerID = models.CharField(max_length=50)
Name = models.CharField(max_length=50)
Mobile = models.CharField(max_length=15)
def __unicode__(self):
return self.CustomerID
class orderHistory(models.Model):
BookingNumber = models.CharField(max_length=50)
customer = models.ForeignKey(Customer)
def __unicode__(self):
return self.BookingNumber
I have also used ModelForm to setup my forms.py:
class customerForm(ModelForm):
class Meta:
model = Customer
exclude = ['CustomerID']
The function in my views.py is something like this:
if request.method == 'POST':
form = customerForm(request.POST or None)
# check whether it's valid:
if form.is_valid():
instance = form.save()
if not instance.CustomerID:
instance.CustomerID = 'DHOBI' + instance.Mobile
instance.save()
return HttpResponseRedirect('/data/test')
else:
form = customerForm()
return render(request, "data/index.html",{'form':form})
My question is, after getting the data for the Customer model, how do I fill the data in BookingNumber in the database? I mean, if I want to set the booking number as Mobile + Today's Date and time.
How do I do that.
import datetime
bn = "%s-%s" % (instance.Mobile,str(datetime.datetime.now())
order_hist = orderHistory.objects.create(customer=instance, BookingNumber=bn)
Just note on naming of classes: give Classes Uppercase names and the attributes lowercase. You have it in reverse.

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .