How to update an object but leave some fields the same - django

So I am making an update form and I want to allow the user to update fields he wants to update and leave the rest alone. But django won't let him leave the build_name field alone with no changes because build_name is a primarykey and has to be unique. However it is the object itself that is triggering the fact that the name already exists.
def update(request, id):
title = 'Updating Build'
the_name = champBuild.objects.filter(pk=id).values_list('build_name', flat=True)
form = champBuildUpdateForm(request.POST or None, initial={'build_name': str(the_name)[3:-2]})
if form.is_valid():
champBuild.objects.filter(pk=id).update(build_name=form.cleaned_data.get('build_name'))
return HttpResponseRedirect('/lolBuilds/detail/{}'.format(form.cleaned_data.get('build_name')))
context = {
'title': title,
'form': form
}
return render(request, 'lolBuilds/update.html', context)
and this is the create.html
<form method='POST' action=''>{% csrf_token %}
{{ form.as_p }}
<input type='submit' value='create'/>
</form>
This is what happens when I click update without changing the Build name field.

Related

Why values are not storing in the table when i have clicked Submit button?

I have created a form for getting the value and placing it in the table. But whenever I click on Submit button, it doesn't store or give any error.It is simply staying in that page itself.
Model.py
class Employee(models.Model):
ename=models.CharField(max_length=120)
eaddress=models.CharField(max_length=120)
eemail=models.CharField(max_length=120)
ephone=models.CharField(max_length=120)
emobile=models.CharField(max_length=120)
eid=models.CharField(max_length=120)
egender=models.CharField(max_length=120)
ehire=models.DateTimeField()
edob=models.DateTimeField()
class Meta:
db_table="employee"
views.py
def employee(request):
emp=Employee.objects.all()
return render(request,'employee.html',{'emp':emp})
def addemployee(request):
if request.method == 'POST':
emp = EmployeeForm(request.POST)
if emp.is_valid():
try:
form.save()
return redirect(employee)
except:
pass
else:
emp = EmployeeForm()
return render(request,'addemployee.html',{'emp':emp})
addemployee.html:
<form method="POST" action="add_employee">
{% csrf_token %}
{{emp.ename}}
{{emp.eemail}}
{{emp.emobile}}
<button type="submit">Submit</button>
</form>
You need to display your form errors in template. So update your view and template like this:
def addemployee(request):
emp = EmployeeForm(request.POST or None)
if request.method == 'POST':
if emp.is_valid():
try:
emp.save()
return redirect(employee)
except Exception as e:
raise e # for debug purpose now
return render(request,'addemployee.html',{'emp':emp})
addemployee.html:
<form method="POST" action="add_employee">
{% csrf_token %}
{{ emp.errors }} // it will render form errors
{{emp.ename}}
{{emp.eemail}}
{{emp.emobile}}
<button type="submit">Submit</button>
</form>
I am assuming your form is not validating because you have many fields like eid, egender etc which are required for saving it in Database. If you are using Modelform, then you can use {{ emp.as_p }} as per form rendering documentation for rendering form instead of {{emp.ename}} {{emp.eemail}} {{emp.emobile}}.

Django form with multiple checkboxes

I am really new to Django! I have a page that displays items with checkboxes next to them. The number of items/checkboxes varies. When a button is pressed, I want the corresponding checked item to be modified.
So far, I have tried to wrap it all in one form:
<form method="post">
{% csrf_token %}
{% for event in items %}
{{ event.eventID }}
<input type="checkbox" value="{{ event.eventID }}" name="choices">
{% endfor %}
<button type="submit">Approve</button>
</form>
I want to collect them in a Django form field. I am trying to use ModelMultipleChoiceField:
class ApproveEventForm(forms.Form):
choices = forms.ModelMultipleChoiceField(queryset = Event.objects.all(), widget=forms.CheckboxSelectMultiple())
And in my views, I want to edit the selected items:
def approve_event(request):
if request.method == "POST":
form = ApproveEventForm(request.POST)
print(form.errors)
if form.is_valid():
for item in form.cleaned_data['choices']:
item.approved = True
item.save()
else:
form = ApproveEventForm()
unapproved = Event.objects.filter(approved=False)
return render(request, 'app/approve_event.html', {'items': unapproved, 'form': form})
My form is not valid and form.errors prints: choices "" is not a valid value for a primary key.
How can I fix this? Or is there another way to access the selected items?
Edit: passed the form to the template.
Managed to fix it using MultipleChoiceField instead of ModelMultipleChoiceField. Then populated the choices with existing event IDs and passed it to the template.
In forms:
choices = forms.MultipleChoiceField(widget = forms.CheckboxSelectMultiple())
In views:
form.fields['choices'].choices = [(x.eventID, "Event ID: " + x.eventID) for x in unapproved]
Had to change some of the logic for finding and editing Event objects too.

form wizard initial data for edit not loading properly in Django?

I have a three page form-list coming out of a single model. I could save the model first time, but when I want to edit the model, only the first form shows the initial value, subsequent forms does not show the initial data. but when I print the initial_dict from views, I can see all the initial views correctly. I followed this blog on form wizard.
Here is my model.py:
class Item(models.Model):
user=models.ForeignKey(User)
price=models.DecimalField(max_digits=8,decimal_places=2)
image=models.ImageField(upload_to="assets/", blank=True)
description=models.TextField(blank=True)
def __unicode__(self):
return '%s-%s' %(self.user.username, self.price)
urls.py:
urlpatterns = patterns('',
url(r'^create/$', MyWizard.as_view([FirstForm, SecondForm, ThirdForm]), name='wizards'),
url(r'^edit/(?P<id>\d+)/$', 'formwizard.views.edit_wizard', name='edit_wizard'),
)
forms.py:
class FirstForm(forms.Form):
id = forms.IntegerField(widget=forms.HiddenInput, required=False)
price = forms.DecimalField(max_digits=8, decimal_places=2)
#add all the fields that you want to include in the form
class SecondForm(forms.Form):
image = forms.ImageField(required=False)
class ThirdForm(forms.Form):
description = forms.CharField(widget=forms.Textarea)
views.py:
class MyWizard(SessionWizardView):
template_name = "wizard_form.html"
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT))
#if you are uploading files you need to set FileSystemStorage
def done(self, form_list, **kwargs):
for form in form_list:
print form.initial
if not self.request.user.is_authenticated():
raise Http404
id = form_list[0].cleaned_data['id']
try:
item = Item.objects.get(pk=id)
###################### SAVING ITEM #######################
item.save()
print item
instance = item
except:
item = None
instance = None
if item and item.user != self.request.user:
print "about to raise 404"
raise Http404
if not item:
instance = Item()
for form in form_list:
for field, value in form.cleaned_data.iteritems():
setattr(instance, field, value)
instance.user = self.request.user
instance.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list], })
def edit_wizard(request, id):
#get the object
item = get_object_or_404(Item, pk=id)
#make sure the item belongs to the user
if item.user != request.user:
raise HttpResponseForbidden()
else:
#get the initial data to include in the form
initial = {'0': {'id': item.id,
'price': item.price,
#make sure you list every field from your form definition here to include it later in the initial_dict
},
'1': {'image': item.image,
},
'2': {'description': item.description,
},
}
print initial
form = MyWizard.as_view([FirstForm, SecondForm, ThirdForm], initial_dict=initial)
return form(context=RequestContext(request), request=request)
template:
<html>
<body>
<h2>Contact Us</h2>
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
{% for field in form %}
{{field.error}}
{% endfor %}
<form action={% url 'wizards' %} method="post" enctype="multipart/form-data">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">"first step"</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">"prev step"</button>
{% endif %}
<input type="submit" value="Submit" />
</form>
</body>
</html>
EDIT:
one this I noticed is the following:
On the edit mode, i.e, when I am at the following url : http://127.0.0.1:8000/wizard/edit/1/,
it displays the first form data correctly, and when I click submit, it is not taking me to step-2 of edit mode, i.e the URL changes to http://127.0.0.1:8000/wizard/create/.
If upon clicking submit on edit url (like /wizard/edit/1) in the first step, same url is maintained then the form would get its initial data in next step. but I cannot figure out how to avoid the url from changing to /wizard/create
The error looks trivial. In your template the form action has wizards url, which is url of create view. Hence when the form is submitted it goes to /wizard/create.
To able to use the template for both views, you can remove the action attribute from form tag. The form will be submitted to current url which can be create or edit.
So change your template to have form tag as
<form method="post" enctype="multipart/form-data">
EDIT: To save item
Update your view as:
def done(self, form_list, **kwargs):
for form in form_list:
print form.initial
if not self.request.user.is_authenticated():
raise Http404
id = form_list[0].cleaned_data['id']
try:
item = Item.objects.get(pk=id)
print item
instance = item
except:
item = None
instance = None
if item and item.user != self.request.user:
print "about to raise 404"
raise Http404
if not item:
instance = Item()
#moved for out of if
for form in form_list:
for field, value in form.cleaned_data.iteritems():
setattr(instance, field, value)
instance.user = self.request.user
instance.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list], })

Django form is not valid

I am trying to submit a form and it will not pass as is_valid in the view.
The form uses forms.Modelforms which I do not have a lot of experience with.
I checked {{ form.errors }} in the template but didn't get anything back.
I appreciate the time and expertise
Form
class AddSubcategory(forms.ModelForm):
class Meta:
model = Subcategory
fields = ['category']
subcategory_name = forms.CharField(max_length=255)
View
#login_required
#locationed
def add_subcategory(request, location):
subcat_form = AddSubcategory(request.POST)
if subcat_form.is_valid():
submitted_subcat_name = subcat_form.cleaned_data['subcategory_name']
selected_cat = subcat_form.cleaned_data['category']
_, was_created = Subcategory.objects.get_or_create(name=submitted_subcat_name, category=selected_cat)
return HttpResponseRedirect(reverse('manage_cat_subcat', args=(location.slug,)))
else:
cat_form = AddCategory()
subcat_form = AddSubcategory()
return render(request, 'inventory/manage_cat_subcat.html', {'location': location,'cat_form': cat_form,'subcat_form':subcat_form})
Template (form)
<form class="form-inline" action="{% url 'add_subcategory' location.slug %}" method="post">
{% csrf_token %}
{{subcat_form.category}}
{{subcat_form.subcategory_name}}
<button class="btn btn-small" type="submit">Add Subcategory</button>
</form>
You specify in fields that you need id, category and name but you don't put them in your form in your template.
You have only category and subcategory_name.
You can add those two elements in your template OR remove them from the fields list.
Also you don't specify an action for your form, you should give the view where the data from your form should be sent.

Django Inline Formset Custom Validation only Validates Single Formset at a time

I'm using Django and have a form with two additional inline formsets. I want to validate that each formset contains at least one populated form. I've written code such that this works but it only works for each formset at a time. If I submit the form without any formset forms populated, only the first one shows a validation error. If I then populate the first formset form, and leave the second one blank, the second one errors.
I want errors to appear on both forms if both are not valid.
The forms are just standard ModelForm instances. Here's my view:
class RequiredBaseInlineFormSet(BaseInlineFormSet):
def clean(self):
self.validate_unique()
if any(self.errors):
return
if not self.forms[0].has_changed():
raise forms.ValidationError("At least one %s is required" % self.model._meta.verbose_name)
def create(request):
profile_form = ProfileForm(request.POST or None)
EmailFormSet = inlineformset_factory(Profile, Email, formset=RequiredBaseInlineFormSet, max_num=5, extra=5, can_delete=False)
email_formset = EmailFormSet(request.POST or None)
PhoneFormSet = inlineformset_factory(Profile, Phone, formset=RequiredBaseInlineFormSet, max_num=5, extra=5, can_delete=False)
phone_formset = PhoneFormSet(request.POST or None)
if profile_form.is_valid() and email_formset.is_valid() and phone_formset.is_valid():
profile = profile_form.save()
emails = email_formset.save(commit=False)
for email in emails:
email.profile = profile
email.save()
phones = phone_formset.save(commit=False)
for phone in phones:
phone.profile = profile
phone.save()
messages.add_message(request, messages.INFO, 'Profile successfully saved')
return render_to_response(
'add.html', {
'profile_form': profile_form,
'email_formset': email_formset,
'phone_formset': phone_formset
}, context_instance = RequestContext(request)
)
And here's my template's form, incase it's useful:
<form action="" method="post" accept-charset="utf-8">
{{ email_formset.management_form }}
{{ phone_formset.management_form }}
{{ profile_form|as_uni_form }}
<div class="formset-group" id="email_formset">
{{ email_formset.non_form_errors }}
{% for email_form in email_formset.forms %}
<div class='form'>
{{ email_form|as_uni_form }}
</div>
{% endfor %}
</div>
<div class="formset-group" id="phone_formset">
{{ phone_formset.non_form_errors }}
{% for phone_form in phone_formset.forms %}
<div class='form'>
{{ phone_form|as_uni_form }}
</div>
{% endfor %}
</div>
<input type="submit" value="Save Profile" id="submit">
</form>
call the is_valid() function for each form that you want validation to occur on. In your example you do if a.is_valid and b.is_valid anc c.is_valid... If a is false, b and c will never get called. Try something different, like:
alpha=a.is_valid()
beta=b.is_valid()
gamma=c.is_valid()
if alpha and beta and gamma:
do stuff
I had a similar issue and the problem was that extra forms were not being validated due to how Django handles extra form fields. Take a look: Django Formset.is_valid() failing for extra forms