Updating form in Django. How to design? - django

I'm a django newbie.
I have this model:
class Item(models.Model):
name = models.CharField(max_length=255)
quantity = models.IntegerField()
How to create view to update quantity of my all Item?
views:
def item_list(request):
item = Product.objects.all()[:6]
return render_to_response('item.html',{'item':item},context_instance=RequestContext(request))
form:
from django import forms
class QuantityForm(forms.Form):
quan = forms.IntegerField()
template:
{% for i in item %}
{{ i.name }}
{{ i.quantity }}
{% endfor %}
I'm trying to do something like this(after clicking "update" value quantity in my model should be actualize):
Please any help. Thanks

First you need a view, which retrieves item id and quantity value, updates relative Item instance and redirects you back to the page. Here is an example:
from django.views.decorators.http import require_http_methods
from django.shortcuts import redirect, get_object_or_404
#require_http_methods(["POST"])
def update_item(request)
id = request.POST.get('id', None) #retrieve id
quantity = request.POST.get('q', None) #retrieve quantity
item = get_object_or_404(Item, id=id) #if no item found raise page not found
if quantity:
#updating item quantity
item.quantity = quantity
item.save()
return redirect('my-item-list-view-name')
Also you need to create urlpattern for the view in your urls.py. For example:
...
url(r'^update-item/$', 'update_item', name='update_item'),
...
Then you can make a forms for each item on a template:
{% for i in item %}
<form action="{% url 'update_item' %}" method="post">
{% csrf_token %}
{{ i.name }}
<input name="id" type="hidden" value="{{ i.id }}" />
<input name="q" type="text" value="{{ i.quantity }}" />
<input type="submit" value="update" />
</form>
{% endfor %}
I'm trying to offer a solution as simple as possible. You should know that django provides a lot of cool stuff, that can help your to solve your problem much efficiently, such as forms, modelforms, formsets...

You can either create a ModelForm for your item and then use Formsets or if you can use jquery to submit a ajax request to a django view that updates the item for the selected model
$('<yourbuttonclass').onClick(function(e){
e.preventdefault()
$.post(urlToPost, dataFromTextField&csrfmiddlewaretken={{csrf_token}})
.success('Successfully Updated')
....
In your view:
#Get the item id from urlconf
#ajax_request
def update_view(request, item_id)
#Update your item
return {'Success': 'Updated to blah'}
I like to use the ajax_request decorator from here for sending ajax response. You can also send a HTTPResponse('Successfully updated')
If you want to creating a Restful resource would be a great way too and then you get an interface to create, update, read and delete from a single pattern. Read up on Django Tastypie

in views.py :
if request.method=='POST':
if 'txt1' in request.POST:
if request.POST['txt1']!='':
obj=Item.objects.get(pk=request.POST['item1'])
obj.quantity=request.POST['txt1']
obj.save()
if 'txt2' in request.POST:
if request.POST['txt2']!='':
obj=Item.objects.get(pk=request.POST['item2'])
obj.quantity=request.POST['txt2']
obj.save()
if 'txt3' in request.POST:
if request.POST['txt3']!='':
obj=Item.objects.get(pk=request.POST['item3'])
obj.quantity=request.POST['txt3']
obj.save()
#continue this code for all 6 items
update:
of course U can put this in a loop:
for i in range(1,6):
if 'txt'+str(i) in request.POST:
if request.POST['txt'+str(i)]!='':
obj=Item.objects.get(pk=request.POST['item'+str(i)]
obj.quantity=request.POST['txt'+str(i)]
obj.save()
in template:
<form method='POST' action=''>
{% for i in item %}
{{ i.name }}:<input type='text' id='txt{{forloop.counter}}' value='{{ i.quantity }}' /><input type='hidden' id='item{{forloop.counter}}' value='{{item.pk}}' /><input type='submit' value='increase' id='sbm{{forloop.counter}}' />
{% endfor %}
</form>
update: forloop.counter is the current for counter,1,2,3...

Related

How to get information from option field in django?

I have django application with authentication and I have dropdown menu inside.
Before I did it like:
<select name="country" id="id_category" data="{{ data.country }}">
{% for each in living_countries_list %}
<option name="country" value="{{ each.0 }}" class="living_countries">{{ each.1 }}</option>
% endfor %}
</select>
And now I changed it to:
<input list="brow" placeholder="Search for your country..." class="input_country">
<datalist id="brow">
{% for each in living_countries_list %}
<option name="country" value="{{ each.0 }}" class="living_countries">{{ each.1 }}</option>
{% endfor %}
</datalist>
<p class="country_text">Please select your living country</p>
In my views.py file I passed context like:
country = request.POST.get('country')
professors = models.Professor.objects.all()
living_countries_list = LIVING_COUNTRIES
print(country)
In models.py I have options like:
LIVING_COUNTRIES = [
('AFGANISTAN', 'Afganistan'),
('ALBANIA', 'Albania'),
('ALGERIA', 'Algeria'),
('ANGORRA', 'Andorra'),
('ANGOLA', 'Angola')]
class Professor(models.Model):
country_living = models.CharField(max_length=50, choices=LIVING_COUNTRIES, default=FRESHMAN, blank=True, null=True)
So I have few options which are displayed either way but in changed I can type in input and that's what I want to be able to do.
If you want me to post anything else let me know in comments
You don't need to create forms with traditional methods in django. Create first FormSet in views.py:
from django.shortcuts import render
from . import models
from django.forms import modelformset_factory
def formpageview(request):
ProfessorFormSet = modelformset_factory(models.Professor, fields=('country_living',))
queryset = models.Professor.objects.filter(pk=1) # you can make the filter whatever you want it doesn't have to be that way
if request.method == 'POST':
formset = ProfessorFormSet(request.POST, request.FILES, queryset=queryset,)
if formset.is_valid():
formset.save()
# do something.
else:
formset = ProfessorFormSet(queryset=queryset,)
return render(request, 'form.html', {'formset': formset})
and then use this formset in form.html:
<form method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<button type='submit'>Submit</button>
</form>
You should review the documentation

django form populate multiple identical field form in one submit

I don't want to use django form class as they will not give me much flexibility.
I have a form where will random number field in easy request. i am trying to populate the multiple value of forms that appears.
this is my models.py
class Profile(models.Model):
name = models.CharField(max_length=100)
photo = models.FileField()
and this my form.html
<form method="POST" action="{% url 'form' %}">
{% csrf_token %}
{% for i in range %}
<input type="text" id="name" name="name"><br>
<input type="file" id="photo" name="photo"><br><br>
{% endfor %}
<input type="submit" value="Submit">
</form>
You may notice I am rendering field with for loop.
that is means, there will be as much field as possible can be appear based on user request.
So I want to populate these models.
my view looks like
def form_view(request):
if request.method == 'POST':
# Need to puplate the form
return render(request, 'form.html', {'range': range(40)})
Can anyone please help me how can i achieve this? i am just struggling to achieve this.
you can use modelformset_factory for this. this way,
in your views.py
from .models import Profile
from django.forms import modelformset_factory
def form_view(request):
form_range = 40 # set the range here
ProfileFormSet = modelformset_factory(Profile, fields=("name", "photo"), extra=form_range, max_num=form_range)
formset = ProfileFormSet(request.POST or None)
if request.method == "POST":
if formset.is_valid():
formset.save()
return render(request, "form.html", {"profile_formset": formset})
and in your form html
<form method="POST" action="{% url 'form' %}">
{% csrf_token %}
{{ profile_formset.as_p }}
<input type="submit" value="Submit">
</form>

Edit Model data using ModelForm: ModelForm validation error

I am working on my first django app. I am building an app that allows the user to rate beer. I want my user to be able to edit an entry they've already created. I take them to a ModelForm, and ask for their entry. When the POST method is called, my data is invalid. Here is my model.py:
from django.db import models
class Rating(models.Model):
beer_name = models.TextField()
score = models.DecimalField(max_digits=2, decimal_places=1)
notes = models.TextField(blank=True)
brewer = models.TextField(blank=True)
and forms.py:
from django import forms
from ratings.models import Rating
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
fields = ['beer_name', 'score', 'notes', 'brewer']
Here is the views.py of my edit function:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
if request.method == "POST":
form = RatingForm(request.POST, instance=rating)
if form.is_valid():
form.save()
return redirect(home)
else:
return HttpResponse("Invalid entry.")
else:
context = {'form': rating}
form = RatingForm(instance=rating)
return render(
request,
'ratings/entry_def.html',
context
)
However, every time the POST is called I get an "Invalid entry." HttpResponse, meaning my form.is_valid() is being returned False. Here is my template:
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Edit Rating</h2>
<form role="form" method="post">
{% csrf_token %}
<p>Beer Name: <textarea>{{ form.beer_name }}</textarea></p>
<p>Score: <input type="text" name="BeerScore" value="{{ form.score }}"></p>
<p>Notes: <textarea>{{ form.notes }}</textarea></p>
<p>Brewer: <textarea>{{ form.brewer }}</textarea></p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
</form>
</div>
</div>
</div>
{% endblock %}
So when I press my Save button, I am getting the response. Here is my edit url in urls.py:
urlpatterns = [
...
url(r'rating/edit/(?P<row_id>[0-9]+)/$', edit , name='rating-edit'),
]
You're wrapping fields in other fields which don't have name attributes. This is most likely causing the values to be excluded from the request.POST data.
Additionally, Django form fields all have a corresponding HTML widget. So there's really no need to render the HTML by hand, unless you need to.
Change your template code to:
<p>
{{ form.beer_name.label }}: {{ form.beer_name }}
{% if form.beer_name.errors %}
<br />{{ form.beer_name.errors }}
{% endif %}{# repeat for other fields as needed #}
</p>
<p>{{ form.score.label }}: {{ form.score }}</p>
<p>{{ form.notes.label }}: {{ form.notes }}</p>
<p>{{ form.brewer.label }}: {{ form.brewer }}</p>
<p><button type="submit" class="save btn btn-primary">Save</button></p>
<p><button type="reset" class="btn btn-primary">Cancel</button></p>
If you need to change the widget, do so at the form class level:
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
def __init__(self, *args, **kwargs):
super(RatingForm, self).__init__(*args, **kwargs)
self.fields['notes'].widget = forms.Textarea()
This way, Django manages the attributes and binding for you.
Your view can also use some cleanup:
def edit(request, row_id):
rating = get_object_or_404(Rating, pk=row_id)
form = RatingForm(request.POST or None, instance=rating)
if request.method == "POST" and form.is_valid():
form.save()
return redirect(home)
context = {'form': rating}
return render(request, 'ratings/entry_def.html', context)

Inject non-form element into dynamic Django form?

Is it possible to inject a non-form element into a dynamic Django form's context? I have a "Delete user photos" form that I want to contain a thumbnail image of each user with a BooleanField checkbox and label right below it:
+------------+
| |
| photo |
| |
+------------+
[x] Delete <username>'s photos
Right now I know how to create the dynamic checkboxes and their labels but I'm not sure how to go about adding each user's photo. As can be seen from my code below, the name attribute of each HTML input tag will contain the user's ID and I'll examine this attribute when the user submits the form to determine whether to delete their photos or not. I'd like to insert an tag just above each input tag that links to the user's profile photo. The image tag's "src" attribute will contain the user's ID which creates the link to their photo. Is there a way to "inject" this non-form image tag into the context of this dynamic form in order to render an image tag just above each checkbox input tag?
Thanks.
# views.py
def remove_access_to_private_photos(request, template):
if request.method == 'POST':
form = RemovePrivatePhotoAccessForm(request.POST, this_user_id=request.user.id)
if form.is_valid():
for name, value in form.cleaned_data.items():
if value == True:
# Profile links to User via a OneToOneField
this_user = Profile.objects.get(user_id=request.user.id)
other_user = Profile.objects.get(user_id=name)
this_user.remove_private_access(other_user_prof)
return redirect('photos-home')
else:
form = RemovePrivatePhotoAccessForm(this_user_id=request.user.id)
context = {'form': form}
return render(request, template, context)
# models.py
class RemovePrivatePhotoAccessForm(forms.Form):
def __init__(self, *args, **kwargs):
this_user_id = kwargs.pop('this_user_id')
super(RemovePrivatePhotoAccessForm, self).__init__(*args, **kwargs)
user = User.objects.get(pk=this_user_id)
user_prof = Profile.objects.get(user=user)
other_user_id_list = user_prof.gave_private_access().values_list('user_id', flat=True)
for id in other_user_id_list:
other_user = User.objects.get(pk=id)
self.fields[str(id)] = forms.BooleanField(required=False)
self.fields[str(id)].label = mark_safe('%s') % (id, this_user_id, other_user.username)
# delete_photos.html
<form action="." method="post">
{% csrf_token %}
{% for field in form %}
{# I'D LIKE TO PUT <IMG> TAG HERE #}
{{ field }} Delete {{ field.label|safe }}'s photos
{% endfor %}
<input type="submit" value="Submit" />
</form>
A form field is just a class, so you can add whatever properties you need when or after you instantiate it. Your view remains unchanged given this example code.
# forms.py
from .models import Profile # or whatever
class RemovePrivatePhotoAccessForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id')
user_profile = Profile.objects.get(pk=user_id)
other_profiles = user_profile.gave_private_access()
for profile in other_profiles:
id = str(profile.id)
field = forms.BooleanField(required=False)
field.label = mark_safe('%s') % (id, user_profile.id, profile.username)
field.photo = profile.photo
self.fields[id] = field
super(RemovePrivatePhotoAccessForm, self).__init__(*args, **kwargs)
# delete_photos.html
<form action="." method="post">
{% csrf_token %}
{% for field in form %}
<img src="{{ field.photo.url }}" />
{{ field }} Delete {{ field.label|safe }}'s photos
{% endfor %}
<input type="submit" value="Submit" />
</form>
Here's how I solved this problem. First I created a custom template tag:
# photos/templatetags/photos_extras.py
from django import template
from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
register = template.Library()
#register.simple_tag
def render_image(uid):
user = User.objects.get(pk=uid)
src_string = ''.join([
'/photos/',
user.username, '_',
user.profile.image_id,
'_thumb.jpg'])
img_tag = ''.join([
'<img src="',
src_string,
'" alt="',
user.username,
'" />'])
return mark_safe(img_tag)
I then inserted the custom template tag into my template. field.name contains the desired user's user ID, and render_image returns the desired HTML img tag.
# delete_photos.html
<form action="." method="post">
{% csrf_token %}
{% for field in form %}
{% render_image field.name %}
{{ field }} Delete {{ field.label|safe }}'s photos
{% endfor %}
<input type="submit" value="Submit" />
</form>

How can I ask for user input with an action in django admin?

In my code, I am writing an action for grouping, I would like to ask the user how many people would they like per group and then respond with an alert box that says something along the lines of you have 4 groups, based on user input. How do I do this in django admin, how do I create some kind of pop up that asks for the amount of people that they would like to put in a group? (I'm trying to achieve this with an action)
admin.py:
Def howmany (modeladmin, request, queryset):
people = queryset.count()
amount_per = [the number that the user inputs]
Amount_of_groups = people/amount_per
A more simplified and better approach I found here:
You just need to create an Action Form like this.
from django.contrib.admin.helpers import ActionForm
from django import forms
class XForm(ActionForm):
x_field = forms.ModelChoiceField(queryset=Status.objects.all(), required=False)
Now, define that XForm in your admin.py
class ConsignmentAdmin(admin.ModelAdmin):
action_form = XForm
actions = ['change_status']
def change_status(modeladmin, request, queryset):
print(request.POST['x_field'])
for obj in queryset:
print(obj)
change_status.short_description = "Change status according to the field"
admin.py Something like:
class MyAdmin(admin.ModelAdmin):
def howmany(modeladmin, request, queryset):
people = queryset.count()
amount_per = [the number that the user inputs]
Amount_of_groups = people/amount_per
if 'apply' in request.POST:
form = AmountPerForm(request.POST)
if form.is_valid():
amount_per = form.cleaned_data['amount_per']
self.message_user(request, u'You selected - %s' % amount_per)
return HttpResponseRedirect(request.get_full_path())
else:
form = AmountPerForm()
return render(request, 'admin/amount_per_form.html', {
'items': queryset.order_by('pk'),
'form': form,
'title': u'Your title'
})
File "admin/amount_per_form.html" contains something like:
{% extends 'admin/base_site.html' %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="howmany" />
{% for item in items %}
<input type="hidden" name="_selected_action" value="{{ item.pk }}"/> {# thanks to #David Hopkins for highligting it #}
{% endfor %}
{{ form }}
<p>Apply for:</p>
<ul>{{ items|unordered_list }}</ul>
<input type="submit" name="apply" value="Apply" />
</form>
{% endblock %}