Is it possible to loop a custom model form in django? - html

Is it possible to create multiple objects for a model in django by looping the same form in a for loop. I am using a custom model form.
My template is:
{% for query in queryset %}
<form method="POST" action="{% url 'academics' %}" style=" padding: 5%">
{% csrf_token %}
<input type="text" name="Student" class="form-control" id="id_Student"
value="{{query}}">
<input type="text" name="Subject" class="form-control" required id="id_Subject">
<input type="checkbox" name="Presence" id="id_Presence">
<button type="Submit" id="submit">Submit</button>
{% endfor %}
<button type="Submit" id="submit">Submit</button>
</form>
My models.py is:
class Attendance(models.Model):
Student = models.CharField(max_length=100, blank=False)
Hour = models.CharField(max_length=1, blank=False)
Subject = models.CharField(max_length=8, blank=False)
Date = models.DateTimeField(default=timezone.now)
Presence = models.BooleanField(default=False, blank=False)
def __str__(self):
return f'{self.Student}'
My views.py is:
def academics(request):
if request.user.is_staff:
form = forms.AttendanceForm()
context = {
'form': form,
'queryset': User.objects.filter(profile__Year='SY',profile__Department='CSE')
}
if request.method == "POST" :
form = forms.AttendanceForm(request.POST)
if form.is_valid():
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student,Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
return render(request, 'console/academics.html',context)
Currently i can create multiple objects, but with the same values of the last form. ie, the object is created with the values of last form. Here i have looped the form so that n number of forms will be generated for n queries with the name filled automatically in the first field. I know explaining this is little complex. Anyone can help?

I'm not entirely clear what you mean by "looping a form", but if you want the user to be able to enter a list of arbitrary length of similar sets of data, then what you want is a Formset or a ModelFormset. When it comes back, you validate all the data that he has submitted, and if it's all good then you iterate through it, usually creating or modifying multiple objects.

Due to reputation I'm unable to comment but I believe this is how you achieve your desired result. by using WHILE LOOP.
I myself have not much knowledge of python & Django but I guess this is the logic. Please correct me if I am wrong instead of down voting.
var = 0
n = 5
if request.method == "POST":
form = forms.AttendanceForm(request.POST)
if form.is_valid():
while var < n:
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student, Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
var += 1
return render(request, 'console/academics.html', context)

Related

How can I edit manytomanyfield data

I am trying to edit manytomanyfield data. Here is my model
class Permission(models.Model):
shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
permission_title = models.CharField(max_length=255)
class Meta:
ordering = ["-id"]
def __str__(self):
return self.permission_title
class Roles(models.Model):
shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
role_title = models.CharField(max_length=255)
permissions = models.ManyToManyField(Permission)
class Meta:
ordering = ["-id"]
def __str__(self):
return self.role_title
Here in these models i have a model called permission. this model is in a manytomanyfield relation with Roles model. I want to edit this manytomanyfield. I have already did the creation part. But now i want to edit the data and I want to show the data in the templates, I chose while creating a role. I want to show it an input type checkox
Here is my views:-
def createRolesView(request, shop_id):
shopId = get_object_or_404(Shop, pk=shop_id)
permissions = Permission.objects.filter(
shop=shopId.id
)
if shopId.user == request.user:
if request.method == "POST":
role_title = request.POST.get("role_title")
shop = Shop.objects.get(id=shopId.id)
permissions = request.POST.getlist("permissions")
rl = Roles(
role_title = role_title,
shop = shop,
)
rl.save()
for p in permissions:
rl.permissions.add(p)
rl.save()
return redirect(f"/adminpanel/roles/{shopId.id}/")
args = {
"shopId": shopId,
"permissions": permissions,
}
return render(request, "roles/create-role.html", args)
else:
return redirect("warning")
def editRolesView(request, role_id, shop_id):
shopId = get_object_or_404(Shop, pk=shop_id)
roleId = get_object_or_404(Roles, pk=role_id)
if shopId.user == request.user:
if request.method == "POST":
roleId.role_title = request.POST.get("role_title")
shop = Shop.objects.get(id=shopId.id)
# roleId.permissions_set.all()
args = {
"shopId": shopId,
"roleId": roleId,
}
return render(request, "roles/edit-role.html", args)
else:
return redirect("warning")
Suggestions only, I haven't coded this myself.
[Edit. This question caused an "itch", so I coded it up to conform I was right. Skip ahead for a pretty generic edit M2M view ]
Two simple tables
One showing the current permissions assigned to the role, which has the queryset roles.permissions.all(), along with a "remove" checkbox for each.
The other showing the available permissions with an "add" checkbox for each. The queryset might be permissions.exclude( roles__pk=this_role.pk) (to exclude those already added, although it's harmless to add something already added). You should also exclude any permissions which this roles instance is not allowed to acquire.
You might use Django Formsets, but it's also rather easy to process raw data from request.POST in this case. Your template would basically be a form containing instances of
<input type="checkbox" name="add" value="{{permission.pk}}" >
and
<input type="checkbox" name="remove" value="{{permission.pk}}" >
with appropriate descriptive text gathered from the permission instance.
If it were me I'd display them as tables left and right, but as long as they are inside a <form> with Submit (and Done?) buttons, it's not relevant.
In your POST handling, you'd get the checked pk values with request.POST.getlist('add') and request.POST.getlist('remove'). For each one, revalidate that it was in the original queryset that was rendered into a form, and if the pk values are acceptable
role.permissions.add( permission) # or .remove( permission)
(where permission is the Permissions instance that's been revalidated)
After Submit of the above you'd probably redirect back to the same again so that the user can see that the requested additions and removals have happened. The "Done" button will redirect elsewhere( if "Done" in request.POST ... )
[Edit] The result of causing me an itch I had to scratch ....
class GenericEditM2MView( DetailView):
#model = Model # required as per DetailView
# template_name = # as per DetailView
#m2m_fieldname = None # no longer required if unique: the name of the model's m2m field to operate on
remove_qs = None # defaults to .all() of the m2m field
success_url = '.' # defaults to displaying the modified m2m relationship unless done
done_url = None # where to go if submit="Done", defaults to success_url
"""
template_name must define a form full of checkboxes, obtained from
{% for p in add_qs %}
<input type="checkbox" name="add" value="{{p.pk}}" > {% endfor}
{% for p in remove_qs %}
<input type="checkbox" name="remove " value="{{p.pk}}" > {% endfor}
default is to return to this same view after submit, to show that the changes
have been made. You can supply <input type="submit" name="submit" value="done" />
which will go to done_url instead of success_url
example use:
class PenStockM2MView( GenericEditM2MView):
template_name = 'playpen/edit_m2m.html'
model = PenStock
# m2m_fieldname = 'name' # works it out if ony one M2M field on the model
done_url = '/playpen/OK'
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
# everything works without this __init__ provided self.m2m_fieldname is present and correct.
# if model has only one m2m field, locate it via _meta as default.
# Also check m2m_fieldname is m2m because very confusing errors later if it's not!
f = getattr(self, 'm2m_fieldname', None)
m2m_fieldnames = [ field.name for field in self.model._meta.get_fields() if field.many_to_many ]
model_name = self.model.__name__
if f and not f in m2m_fieldnames:
raise AttributeError( f'field "{f}" is not a many-to-many field in {model_name}')
if not f:
if len( m2m_fieldnames ) == 1:
self.m2m_fieldname = m2m_fieldnames[0]
else:
raise AttributeError( f'Cannot identify a unique many-to-many field in {model_name}' )
def get_add_queryset(self):
field = getattr( self.object, self.m2m_fieldname)
remove_qs = self.get_remove_queryset()
already_there = remove_qs.values_list('pk', flat=True)
return remove_qs.model.objects.exclude( pk__in = already_there) # is qs.model documented?
def get_remove_queryset(self):
if hasattr( self, 'remove_queryset'):
return self.remove_qs
remove_qs = getattr( self.object, self.m2m_fieldname)
return remove_qs.all()
def get_context_data( self, **kwargs):
context = super().get_context_data( **kwargs)
context['add_qs'] = self.get_add_queryset()
context['remove_qs'] = self.get_remove_queryset()
return context
def post( self, request, *args, **kwargs):
self.object = self.get_object()
add = request.POST.getlist('add')
remove = request.POST.getlist('remove')
add_objs = list( self.get_add_queryset().filter(pk__in=add) )
remove_objs = list( self.get_remove_queryset().filter(pk__in=remove) )
field = getattr( self.object, self.m2m_fieldname )
field.add( *add_objs)
field.remove( *remove_objs)
return HttpResponseRedirect( self.get_done_url() or self.get_success_url() )
def get_success_url(self):
return self.success_url
def get_done_url( self):
done = self.request.POST.get("submit", None)
if done == "done" and hasattr(self, 'done_url'):
return self.done_url
return None
And here's a template (not completely generic. Using Bootstrap in base.html.)
<div class="row">
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Remove </h1>
<table class="table tbl">
{% for p in remove_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="remove" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Add </h1>
<table class="table tbl">
{% for p in add_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="add" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
</div>
<div class="row">
<form method="post" id="the-form"> {% csrf_token %}
<input type="submit" name="submit" value="submit" />
 
<input type="submit" name="submit" value="done" />
</form>
</div>

How do I use a for loop to reuse a django form in a template

After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!

full_clean() missing 1 required positional argument: 'self'

I'm currently using django version 2.2.4 and trying to create an edit button that will update my models. When trying to save the updated value an TypeError occured which it stated that "full_clean() missing 1 required positional argument: 'self'". I can't seem to detect any error from my codes. Thanks in advance for helping me.
my views.py file
def lab_edit(request, pk, template_name='webapp/lab_edit.html'):
lab= get_object_or_404(Labs, pk=pk)
form = LabForm(request.POST or None, instance=Labs)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('lab')
return render(request, template_name, {'form':form})
my lab_edit.html file
<div class='container'>
<h2>EDIT LAB</h2>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
</div>
my LabForm
class LabForm(forms.ModelForm):
class Meta:
model = Labs
fields = ('labcode', 'name','administrator')
Your LabForm gets as instance= the model class, not a model object. You should fix that by passing lab instead:
def lab_edit(request, pk, template_name='webapp/lab_edit.html'):
lab = get_object_or_404(Labs, pk=pk)
if request.method == 'POST':
form = LabForm(request.POST, instance=lab)
if form.is_valid():
form.save()
return redirect('lab')
else:
form = LabForm(instance=lab)
return render(request, template_name, {'form':form})
By passing a reference to the class , you have basically called full_clean on the class, hence the error.
Note that you should not use request.POST or None since an empty POST request can still be a valid POST request.

Passing data from form in html to views.py not working

I passed the id value from html form to views .py. I want to check if the value matches with the one in database. But for some reason it's not working.
list.html
<form method= 'POST' action = "{% url 'jobs:pinned'%}">
{% csrf_token%}
<input type ="text" name = "number">
<input type="submit" value="Submit">
</form>
views.py
def pinned(request,category_slug=None):
users = User.objects.exclude(id=request.user.id)
jobs_list1 = Jobs.objects.all()
if request.method =="POST":
vari = request.GET.get('number')
for evert in jobs_list1:
if evert.Job_Id == vari:
evert.flag = True
evert.save(update_fields=["flag"])
context = {
'job_list1':jobs_list1,
'users':users
}
return render(request, 'jobs/product/list.html',context)
Here, if i put a static value as 511, i.e if evert.Job_Id ==511, it works. But if i change it to request.GET.get('number'), it's not working. How do i send value from form input value to views.py. Thanks.
Firstly method of your form is POST, so for GET method it will work.
for post method try this
vari = request.POST.get('number')
hope it helps
It turns out, I was comparing string with an integer. Thus I solved this problem as:
if request.method =="POST":
vari = request.POST.get('number')
vari = int(vari)
for evert in jobs_list1:
vari1 = evert.Job_Id
.....

Django Model Boolean Field needs to be initially unchecked. initial=False required=False is not working

Forms.py
class CheckPostedForm(forms.ModelForm):
posted = forms.BooleanField(required=False, initial=False)
Views.py
postform = CheckPostedForm(request.POST, instance=author)
if postform.is_valid():
postform.save()
else:
print 'Cannot save post form.'
Models.py
posted = models.CharField(max_length=2, blank=True, null=True)
class Meta:
model = TmpPlInvoice
exclude = ['net_amt', 'post_date', 'address', 'particulars', 'pos_code', 'acct', 'cust', 'voucher_id', 'voucher_date', 'post_date']
labels = {
'posted': 'Posted',
}
I want a checkbox which is initially unchecked and based on th value from DB i check/uncheck it. No matter what i do the form always comes out as checked="checked".
print postform gives this,
<tr><th><label for="id_posted">Posted:</label></th><td><input checked="checked" id="id_posted" name="posted" type="checkbox" value="n" required /></td></tr>
I have searched alot every where, the docs say that it should be initially false required=False if we want to apply conditions.
Detail
Views.py
def master_detail_posted(request):
if request.method == 'GET':
author = TmpPlInvoice.objects.get(id=1)
postform = CheckPostedForm(instance=author)
return render(request,'main.html' ,{'postform': postform})
if request.method == 'POST':
author = TmpPlInvoice.objects.get(id=1)
postform = CheckPostedForm(request.POST, instance=author)
print postform
if postform.is_valid():
logger.info('saving post form %s', postform.cleaned_data)
postform.save()
else:
logger.info('post form is not valid %s %s', postform.errors, request.POST)
return render(request,'main.html' ,{'postform': postform})
Template
<div> {{ postform }} </div>
You've hard coded the field in the template. Clearly, Django can't change HTML tags that you've written explicitly.
You need to use Django tags in your template:
<td>{{ form.posted }}</td>