I am trying to do a form with file uploading. I got "this field is required" even though I choose a file in the form. Below is my code.
It works once before, I don't why it doesn't work suddenly.Thanks in advance.
Model
from django.db import models
from django.contrib.auth.models import User
from time import time
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(time()).replace('.', '_'), filename)
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User)
contact_number = models.CharField(max_length=10)
tag_id = models.CharField(max_length=20)
layout = models.FileField(upload_to=get_upload_file_name)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
Form
from django import forms
from models import UserProfile
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('contact_number','tag_id', 'layout')
View
#login_required
def user_profile(request):
if request.method == 'POST' and request.is_ajax():
form = UserProfileForm(request.POST, request.FILES,instance=request.user.profile)
if form.is_valid():
form.save()
#return HttpResponseRedirect('/accounts/loggedin')
return HttpResponse("Update success")
else:
#return HttpResponseRedirect('/accounts/loggedin')
return HttpResponse("Update error")
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request))
args['form'] = form
args['full_name'] = request.user.username
return render_to_response('profile.html', args)
profile.html
<div align = "center">
<h2 >Profile</h2>
<form id="profile_form" method="POST" action="/accounts/profile/" enctype="multipart/form-data">
{% csrf_token %}
<table>
<tbody>
<tr>
<td>Name:</td>
<td>{{full_name}}</td>
</tr>
<tr>
<td>Tag ID</td>
<td>{{form.tag_id}}</td>
</tr>
<tr>
<td>Contact number</td>
<td>{{form.contact_number}}</td>
</tr>
<tr>
<td>Layout</td>
<td>{{form.layout}}</td>
</tr>
</tbody>
</table>
<input type="submit" id="update" value="Update" />
</form>
<DIV id="saved"></DIV>
</div>
Related
I am having the following models. The ItemSettings model has no records inserted initially. I have an HTML table with a link Rules to insert settings data for each item number in the ItemMaster. While adding ItemSettings details. The ItemSettings model will have its own view to edit the details of the settings, once inserted. I don't want the ItemNumber to be displayed as a select dropdown. There can be only one record in the ItemSettings model. I am unable to achieve adding the record in the ItemSettings models with the below code. What am I doing wrong?
Models.py:
class ItemMaster(models.Model):
project_name = models.ForeignKey(ProjectMaster, null=True, on_delete=models.SET_NULL)
item_number = models.CharField(max_length=15, unique=True, error_messages={
'unique': "This Item Number Already Exists!"})
item_type = models.ForeignKey(ItemType, null=True, on_delete=models.SET_NULL)
item_description = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.item_number
class ItemSettings(models.Model):
item_number = models.OneToOneField(ItemMaster, on_delete=models.CASCADE)
low_at = models.FloatField(default=0)
minimum_value = models.FloatField(default=0)
maximum_value = models.FloatField(default=0)
def __str__(self):
return str(self.item_number)
Views.py:
def itemsettings_edit(request, pkey):
item_master_data = ItemMaster.objects.get(id=pkey)
item_no = item_master_data.item_number
form = ItemSettingsForm()
if request.method == "GET":
return render(request, 'masters/edit.html', {'item_no': item_no})
elif request.method == 'POST':
try:
item_number = request.POST['item_no']
low_at = request.POST['low_at']
minimum_value = request.POST['minimum_value']
maximum_value = request.POST['maximum_value']
form = ItemSettingsForm(request.POST)
ItemSettings(item_number=item_number, low_at=low_at,
minimum_value=minimum_value, maximum_value=maximum_value).save()
messages.SUCCESS(request, 'Data Saved')
except Exception as e:
messages.SUCCESS(request, 'Data Already Added!!!')
return render(request, 'masters/edit.html', {'item_no': item_no, 'form': form})
edit.html:
<form action="" enctype="multipart/form-data" method="POST" novalidate>
{% csrf_token %}
<table>
<tr>
<td>Item No.</td>
<td><input name="item_number" readonly type="text" value="{{ item_no }}"></td>
</tr>
<tr>
<td>Low At</td>
<td><input name="low_at" type="text"></td>
</tr>
<tr>
<td>Minimum</td>
<td><input name="minimum_value" type="text"></td>
</tr>
<tr>
<td>Maximum</td>
<td><input name="maximum_value" type="text"></td>
</tr>
</table>
<div class="card-action">
<button class="btn waves-effect waves-light btn-small" name="action"
type="submit">Save
</button>
<a class="btn grey darken-3 waves-effect waves-light btn-small"
href="{% url 'itemMaster_list' %}">Cancel</a>
</div>
</form>
Maybe when you get data in this way.
item_number = request.POST['item_no']
is not get correctly, because when you want to save data with relations model you should save data as instance from model like this way:
ItemMaster.objects.get(id=request.POST['item_no'])
and pass the result to ItemSettings.item_number.
If you look at it correctly, the name for item_number field is item_number in HTML not item_no, so it should be:
item_number = request.POST['item_number'] #This is the right name.
...
...
Have a master-detail objects with a one-to-many relationship:
from django.db import models
class Master(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.IntegerField()
class Detail(models.Model):
field3 = models.IntegerField()
field4 = models.IntegerField()
field5 = models.IntegerField()
master = models.ForeignKey(Master, on_delete=models.CASCADE)
For details, I have a ModelForm and an inline formset:
from django import forms
from .models import Master, Detail
class MasterForm(forms.Form):
field1 = forms.CharField(label='Field 1', max_length=100)
field2 = forms.IntegerField(label='Field 2')
class DetailsForm(forms.ModelForm):
class Meta:
model = Detail
exclude = ()
DetailsFormset = forms.inlineformset_factory(
Master, Detail, form=DetailsForm, extra=1)
I have a template view:
class MasterDetailsView(TemplateView):
template_name = 'app/master_detailsview.html'
def post(self, request, *args, **kwargs):
print('IN POST!')
details_formset = DetailsFormset(request.POST)
if details_formset.is_valid():
print('FORMSET VALID!')
Master.objects.get(pk=self.kwargs['pk']).save()
details_formset.save()
else:
print('ERRORS!')
print(details_formset.errors)
return HttpResponseRedirect(self.request.path_info)
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
pk = self.kwargs['pk']
master_instance = Master.objects.get(pk=pk)
context['master_instance'] = master_instance
if self.request.POST:
context['details_formset'] = DetailsFormset(self.request.POST, instance=master_instance)
else:
context['details_formset'] = DetailsFormset(instance=master_instance)
return context
and the template:
{% extends 'base.html' %}
{% block contents %}
<table class="table table-bordered">
<tr>
<th>Field 1</th>
<th>Field 2</th>
</tr>
<tr>
<td>{{ master_instance.field1 }}</td>
<td>{{ master_instance.field2 }}</td>
</tr>
</table>
<hr/ >
<form action="" method="post">
{% csrf_token %}
{{ details_formset.as_p }}
<input type="submit" value="Save" />
</form>
<hr/ >
{% endblock %}
The error I get in the console:
[{'master': ['The inline value did not match the parent instance.']}]
I suppose my view is not right. I have tried getting the master record and saving it before the details formset but same error. I am not using CreateView because this is a learning project.
Try adding {{details_formset.management_form}} right under the csrf_token in templates
i am trying to create a student attendance sheet in django using django modelformset_factory...but when i save the formset it thows me the id is not valid here is my implementation
i have two models one StudentAttendance and StudentClass:
1: the StudentAttendance model is responsible for stroring students
attendance data here is the example
class StudentAttendance(models.Model):
classroom_id = models.ForeignKey(ClassRoom, on_delete=models.CASCADE, related_name='student_attendance')
attendance_date = models.DateField()
student_id = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='student_attendance')
status = models.CharField(max_length=20, choices=ATTENDANCE_CHOICES)
comment = models.CharField(max_length=150, blank=True)
#signed_by = models.ForeignKey(Teacher, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.student_id)
2: the StudentClass model is a submodel that maps a student to his respective class
class StudentClass(models.Model):
"""
This is a bridge table to link a student to a class
when you add a student to a class we update the selected class capacity
"""
main_class = models.ForeignKey(ClassRoom, on_delete=models.CASCADE, related_name='class_student')
academic_year = models.ForeignKey(AcademicYear, on_delete=models.CASCADE)
student_id = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='student_class')
#property
def is_current_class(self):
if self.academic_year.is_current_session:
return True
return False
def __str__(self):
return str(self.student_id)
So my forms.py implementation is:
class StudentsAttendanceForm(forms.ModelForm):
class Meta:
model = StudentAttendance
fields = ('status', 'comment')
#exclude = [
#'siqned_by',
#]
On my views.py:
def student_attendance_manager(request):
"""
this function is responsible for querying the attendance parameters and present the student multiple attendance form
"""
if request.method == "POST":
# get the class name , the attendance date and present the attendance form
class_name = get_object_or_404(ClassRoom, pk=request.POST['class_name']) # class name
attendance_date = request.POST['date_field'] # date
# get the students in the class which is current active
student = StudentClass.objects.filter(main_class=request.POST['class_name'])
# modelform creation
AttendanceFormSet = modelformset_factory(StudentAttendance, form=StudentsAttendanceForm, extra=0)
# initiate the form and pass in the required parameters ie: classroom_id, attendance_date
list_formset = AttendanceFormSet(queryset=student)
# initialise the class_name and attendance date
#for form_inst in list_formset:
#form_inst.fields['classroom_id'].initial = class_name
#form_inst.fields['attendance_date'].initial = attendance_date
template = 'attendance/students_attendance_form.html'
context = {
'class_name':class_name,
'attendance_form': list_formset,
}
return JsonResponse({'html_form': render_to_string(template, context, request=request)})
template = 'attendance/students_attendance_manager.html'
class_date_selector_form = ClassroomDateQueryForm(request.GET or None)
context = {
'choice_form':class_date_selector_form
}
return render(request, template, context)
when the User Posts the form to be submited this is how i handle the form:
def student_attendance_register(request):
if request.method == "POST":
students = StudentClass.objects.filter(main_class=request.GET['class_id'])
StudentsAttendanceFormSet = modelformset_factory(StudentAttendance, form=StudentsAttendanceForm, extra=0)
list_formset = StudentsAttendanceFormSet(request.POST, queryset=students)
if list_formset.is_valid():
list_formset.save()
return HttpResponse('valid')
else:
return HttpResponse(list_formset.errors)
on my template i display the form in a table and this is my implementation:
form.html:
<form class="js-mark-attendance" method="post" action="{% url 'attendance:student_attendance_register' %}?class_id={{ class_name.id }}">
{% csrf_token %}
<table class="table-striped table table-bordered" id="Student_attendance_table">
<thead>
<tr>
<th>#</th>
<th>Admission Number</th>
<th>Name</th>
<th>Status</th>
<th>Comment</th>
</tr>
</thead>
<tbody>
{{ attendance_form.management_form }}
{% for form_inst in attendance_form %}
{% for hidden in form_inst.hidden_fields %}
{{ hidden }}
{% endfor %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ form_inst.instance.student_id.admission_number }}</td>
<td>{{ form_inst.instance.student_id }}</td>
<td>{{ form_inst.status }}</td>
<td> {{ form_inst.comment }}</td>
{{ form_inst.classroom_id.as_hidden }}
{{ form_inst.attendance_date.as_hidden }}
{{ form_inst.student_id.as_hidden }}
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-md-12 d-flex justify-content-center">
<input type="submit" value="Mark Attendance" class="btn btn-success">
</div>
</div>
</form>
and this is the error that django throws after the user has clicked submit button:
id
Select a valid choice. That choice is not one of the available choices.
so my question is ... how can i handle this post request form and or if their is an alternative way of doing my task:
any leads will be much upreciated
oops....I found my problem was the wrong usage of modelsfomset_factory...
My aim is to use Two models in One template. I have tried various ways around this and have had no success. Originally I had 2 views, 2 models and Two forms. After searching I found people using inlineformsets. So I dropped One of the Views and set-up the inlineformset.
This is currently where I am up to and seem to be hitting a wall.
The template renders to the browser and the 'object_list' part displays the database content as desired and the 'form' part renders the form and validates/saves the data correctly. The issue is with the 'formset'. No fields are rendered (I would expect to see a dropdown as the field is a foreignkey) and when the 'submit' button is pressed I get:
AttributeError at /settings/
'NoneType' object has no attribute 'save'
Any help in finding the error or pointers on alternative solutions would be greatly appreciated.
The Code:
models.py
from django.db import models
class RevisionSettings(models.Model):
global_revision_type = models.CharField(max_length = 5, unique=True, blank = True)
global_revision_description = models.CharField(max_length = 300, unique=True, blank = True)
class Meta:
ordering = ["global_revision_type"]
def __unicode__(self):
return u'%s %s' % (self.global_revision_type, self.global_revision_description)
class RevisionDefaultType(models.Model):
defaultrevisiontype = models.ForeignKey(RevisionSettings)
class Meta:
ordering = ["defaultrevisiontype"]
def __unicode__(self):
return unicode(self.defaultrevisiontype)
views.py
class RevisionSettingsView(CreateView):
template_name = 'settings/revisionsettings_view.html'
model = RevisionSettings
form_class = SettingsForm
success_url = reverse_lazy('globalsettings')
success_message = 'Successfully added your new revision type'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SettingsFormSet(instance = RevisionSettings)
return self.render_to_response(
self.get_context_data(form=form,
formset=formset))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SettingsFormSet(self.request.POST)
if 'rev_settings_form_1' in self.request.POST:
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
elif 'rev_settings_form_2' in self.request.POST:
if formset.is_valid():
return self.formset_valid(formset)
else:
return self.form_invalid(formset)
def form_valid(self, form):
self.object = form.save()
self.object.save()
return HttpResponseRedirect(self.get_success_url())
def formset_valid(self, formset):
self.object.save()
formset.instance = self.object
formset.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form,formset=formset))
def get_context_data(self, **kwargs):
kwargs['object_list'] = RevisionSettings.objects.order_by('global_revision_type')
return super(RevisionSettingsView, self).get_context_data(**kwargs)
forms.py
from django import forms
from django.forms.models import inlineformset_factory
from .models import RevisionSettings, RevisionDefaultType
class SettingsForm(forms.ModelForm):
class Meta:
model = RevisionSettings
class DefaultSettingsForm(forms.ModelForm):
class Meta:
model = RevisionDefaultType
SettingsFormSet = inlineformset_factory(RevisionSettings, RevisionDefaultType)
revisionsettings_view.html
(I have removed most of the HTML styling to keep the information to the point)
{% extends 'base_private.html' %}
{% block content %}
{% for object in object_list %}
<tr>
<td align="center">{{ object.global_revision_type }}</td>
<td align="center">{{ object.global_revision_description }}</td>
<td align="center"><span class="glyphicon glyphicon-remove-circle"></span></td>
</tr>
{% endfor %}
<form action = '{{ action }}' method = 'POST' class="form-horizontal" role="form">
{% csrf_token %}
<tr>
<td align="center">
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.defaultrevisiontype.label_tag }}
{{ form.defaultrevisiontype }}
{% endfor %}
</td>
</tr>
<span class="input-group-addon">
<input type = 'submit' name = 'rev_settings_form_2' value = 'Update Default Revision Type' class = 'btn btn-success'>
</span>
<td align="center">{{ form.global_revision_type }}{{ form.global_revision_type.errors }}</td>
<td align="center">{{ form.global_revision_description }}{{ form.global_revision_description.errors }}</td>
</tr>
<span class="input-group-addon">
<input type = 'submit' name = 'rev_settings_form_1' value = 'Add Revision Type' class = 'btn btn-success'>
</span>
</form>
{% endblock %}
Formsets are overkill for two forms. This is actually not too hard but poorly documented. You can make both forms the same form type, just give a prefix.
def parent_apply(request):
if request.method == 'POST':
parent_form = SignupForm(request.POST, prefix="parent")
student_form = StudentApplyForm(request.POST, prefix="student")
if parent_form.is_valid() and student_form.is_valid():
parent = parent_form.save()
student = student_form.save(parent)
else: messages.error(request, "Please correct the errors marked in red.")
else:
parent_form = SignupForm(prefix="parent")
student_form = StudentApplyForm(prefix="student")
return render_to_response('template_path/forms.html', { 'parent_form':parent_form, 'student_form':student_form }, context_instance=RequestContext(request))
The forms are just regular Django forms, no special settings required. You can change the order on which they validate and save one even if the other did not validate if you choose.
In your HTML Template, wrap both forms in the same tag and they will submit at the same time. If you want your forms to go to different view functions, specify two different elements.
Thanks for all the help. The pointers really helped me come to this solution. The main change was to 'def get' as shown below. I dropped the formset and passed the forms this way.
def get(self, request, *args, **kwargs):
form = self.settings_form_class
formset = self.default_form_class
return self.render_to_response(self.get_context_data(form = form, formset = formset))
I was unaware this was possible! Thanks again.
have a form by which user can enter details about some expenses but i want to have same row in the form again and again but couldn't find out how to do that :
if you see figure above this forms works well for 1 row of data , saves well but with more then 1 row it cant . Can someone suggest any way to do that . Below are the codes :
models.py
from django.db import models
class Expenditure(models.Model):
exp_date = models.DateField("Expenditure_Date")
description = models.CharField(max_length=500)
amount = models.FloatField(default=0)
currency = models.CharField(max_length=15,default="USD")
class Meta:
unique_together = ('exp_date', 'description',)
def __unicode__(self):
return self.description
forms.py
from django import forms
from moni.models import Expenditure
from django.contrib.admin.widgets import AdminDateWidget
class ExpenditureForm(forms.ModelForm):
#exp_date = forms.DateField(help_text="Date")
exp_date = forms.DateField(widget=AdminDateWidget)
description = forms.CharField(max_length=500)
amount = forms.FloatField(initial=0)
currency = forms.CharField(widget=forms.HiddenInput(), initial="USD")
# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
model = Expenditure
fields = ('exp_date', 'amount', 'description')
views.py
from django.template import RequestContext
from django.shortcuts import render_to_response
from moni.models import Expenditure
from moni.forms import ExpenditureForm
def add_expenditure(request):
context = RequestContext(request)
if request.method == 'POST':
form = ExpenditureForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print form.errors
else:
form = ExpenditureForm()
return render_to_response('moni/add_expenditure.html', {'form': form}, context)
add_expenditure.html
{% extends 'moni/base.html' %}
{% block title %}Add Shipment {% endblock %}
{% block body_block %}
<h1>Add a Expenditure</h1>
<p id="p_hide"> I am a paragraph to be hidden</p>
<button id ="btn1">Hide Paragraph</button>
<form id="expenditure_form" method="post" class="vDateField" action="/moni/add_expenditure/">
{% csrf_token %}
<table border=1>
<tr><th><label >Date:</label></th> <th><label for="id_description">Description:</label></th><th><label for="id_amount">Amount:</label></th></tr>
<tr><td><input class="vDateField" name="exp_date" size="10" type="text" /></td><td>{{form.description}}</td><td>{{form.amount}}<input id="id_currency" name="currency" type="hidden" value="MYR" /></td></tr>
<tr><td><input class="vDateField" name="exp_date" size="10" type="text" /></td><td>{{form.description}}</td><td>{{form.amount}}<input id="id_currency" name="currency" type="hidden" value="MYR" /></td></tr>
</table>
<input type="submit" name="submit" value="Create Expenditure" />
</form>
{% endblock %}
For that use Formeset function, Here is the idea for print form in multiple times
ExpenditureFormSet = formset_factory(ExpenditureForm, extra=3,)
And views like
if formset.is_valid():
for data in formset.cleaned_data:
And pass it into {formset} So html will print the extra 3 forms
You should use ModelFormSets instead of ModelForm.
And if you're going to add forms dynamically, use corresponding JavaScript plugin (since management form should be changed every time new form is added).