what is Django formsets used for? - python

I found something strange in Django's documentation which is called formset, see it here.
I am wondering What is formset used for and How to use it?

Formset is an example of datagrid .
If you want to use multiple form of same type at one page.you can use Formset.
Example :
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username", "email"]
Now if you want to use UserForm multiple times at one page you need to use Formset.
from django.forms.formsets import formset_factory
Uforms = formset_factory(UserForm, extra = 4) #extra used to define how many empty forms will display
Into Your views.py
def submit(request):
if request.POST:
#code to manage post request
# validation to formset you can follow django docs
else:
address_formSet = Uforms(instance=UserForm())
# render response
Template code
{{ address_formset.as_table }}
You can follow step by step django formset to learn.
Example Code
class Address(models.Model):
city = models.CharField(max_length=48)
zipcode = models.IntegerField(max_length=5)
class Friend(models.Model):
name = models.CharField(max_length=30)
address = models.ForeignKey(Address)
forms.py
from django import forms
from .models import Address, Friend
from django.forms.models import inlineformset_factory
MAX_ADDRESS = 2 #updated
AddressFormSet = inlineformset_factory(Address, Friend, extra=MAX_ADDRESS) #updated
class UserAddressForm(forms.ModelForm):
class Meta:
model = Address
view.py
from django.shortcuts import render_to_response
from .models import *
from .forms import UserSubmittedAddressForm, AddressFormSet
def submit(request):
if request.POST:
#Logic
else:
form = UserAddressForm()
address_formSet = AddressFormSet(instance=Address())
# render response
template code
{{ form.as_table }}
{{ address_formset.as_table }}

It's used to work with e.g. a table of records. It's a way to create data grid functionality in such a way that Django does all the heavy lifting (all data for all the records are sent back in the same POST).

Related

how can I render a view only when a certain field is True? - Django

I have created detailed views for the posts in my Django-based blog using the DetailView generic class and everything works fine so far. The problem, however, is that I have a field in my post model that is used to set the status of the posts (active, inactive, blocked) and I only want to render the detailed views when the status is active. If anyone knows of a way to achieve this, please let me know and I ask that you be as detailed as possible.
views.py
from .models import Post
from django.views.generic import DetailView
class PostDetailView(DetailView):
model = Post
context_object_name = 'post'
template_name = 'blog/post-detail.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['title'] = Post.objects.filter(slug=self.object.slug).first()
return context
In your DetailView you can filter the queryset, for example you can filter with:
from django.views.generic import DetailView
class PostDetailView(DetailView):
queryset = Post.objects.filter(active=True)
# …
The DetailView will retrieve the element based on the primary key and/or slug on the queryset, so if the element is not in the filtered queryset, then you will retrieve a 404 error.
Here we assume that the Post model has an active field:
class Post(models.Model):
# …
active = models.BooleanField()
# …
Given the field and values are different, you should of course filter the queryset accordingly.

Django; how to forward last form's details to the next? [duplicate]

I would like to create a mutli-step form in Django that only submits the data for processing at the end of all the steps. Each step needs to be able to access and display data that we entered in previous step(s).
Is there a way to do this with Django? Django's Form-Wizard can't handle this basic functionality.
Of course there's a way to do this in Django.
One way is to hold your values in session until you submit them at the end. You can populate your forms using values held in session if you return to previous step.
With some searching, you may find an app that someone has already written that will do what you want, but doing what you need isn't hard to do with Django, or any other framework.
Example, ignoring import statements:
#models/forms
class Person(models.Model):
fn = models.CharField(max_length=40)
class Pet(models.Model):
owner = models.ForeignKey(Person)
name = models.CharField(max_length=40)
class PersonForm(forms.ModelForm):
class Meta:
model = Person
class PetForm(forms.ModelForm):
class Meta:
model = Pet
exclude = ('owner',)
#views
def step1(request):
initial={'fn': request.session.get('fn', None)}
form = PersonForm(request.POST or None, initial=initial)
if request.method == 'POST':
if form.is_valid():
request.session['fn'] = form.cleaned_data['fn']
return HttpResponseRedirect(reverse('step2'))
return render(request, 'step1.html', {'form': form})
def step2(request):
form = PetForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
pet = form.save(commit=False)
person = Person.objects.create(fn=request.session['fn'])
pet.owner = person
pet.save()
return HttpResponseRedirect(reverse('finished'))
return render(request, 'step2.html', {'form': form})
We'll assume that step2.html has a link to go back to step1.html.
You'll notice in the step1 view I'm pulling the value for fn from session that was set when the form was saved. You would need to persist the values from all previous steps into the session. At the end of the steps, grab the values, create your objects and redirect to a finished view, whatever that might be.
None of this code has been tested, but it should get you going.
You can easily do this with the form wizard of django-formtools. A simple example would be something like the following.
forms.py
from django import forms
class ContactForm1(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.EmailField()
class ContactForm2(forms.Form):
message = forms.CharField(widget=forms.Textarea)
views.py
from django.shortcuts import redirect
from formtools.wizard.views import SessionWizardView
class ContactWizard(SessionWizardView):
def done(self, form_list, **kwargs):
do_something_with_the_form_data(form_list)
return redirect('/page-to-redirect-to-when-done/')
urls.py
from django.conf.urls import url
from forms import ContactForm1, ContactForm2
from views import ContactWizard
urlpatterns = [
url(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
]

Django Model Form: show query form and handle post request for OneToMany fields

I am a newbie to Django and could not find similar questions after searching on google/SO.
I've a model named Questions, which has multiple(2-4) choices and defined as below:
class Question(models.Model):
name = models.CharField(max_length=128)
class Choice(models.Model):
name = models.CharField(max_length=256)
is_correct = models.BooleanField(default=False)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
Of the multiple choices only one is correct.
What I want to do: In just one page, user could submit a question together with multiple choices, here is a draft of UI:
My first question: I've defined ModelForm but don't know how to add "choices" field to QuestionForm:
class QuestionForm(ModelForm):
name = forms.CharField(max_length=128)
description = forms.CharField(max_length=256)
class Meta:
model = Question
fields = ['name', 'description']
class ChoiceForm(ModelForm):
name = forms.CharField(max_length=256)
is_correct = forms.BooleanField()
class Meta:
model = Choice
fields = ['name', 'is_correct']
Is it possible to use ModelForm the render the above HTML page besides writing it manually?
My second question: If use clicks "Submit" button, I use AJAX to send json data to backend server, here is an example of form data:
name:question1
choices[][name]:choice1
choices[][is_correct]:1
choices[][name]:choice2
choices[][is_correct]:0
And this is my code handling the request:
form = QuestionForm(request.POST)
if form.is_valid():
question = form.save()
How to parse choices from the request?
How could I parse data of multiple choices part from the POST request?
Again, I'm a newbie to Django and any answers/suggestions is highly appreciated.
To create forms for models which have a OneToMany relation I would recommend you to use Django's inline formsets: https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#inline-formsets
It's a really simple and elegant way to create forms for related models.
To parse the choices, the user entered you could just override the clean method of your form. In this the user content is usually checked and prepared for storing it to the database. https://docs.djangoproject.com/en/1.8/ref/forms/validation/#form-field-default-cleaning
So cleaning could look like this:
class QuestionForm(ModelForm):
...
def clean(self):
cleaned_data = super(QuestionForm, self).clean()
if cleaned_data['choice_name'].startswith('Something'):
raise forms.ValidationError(
"Choice names cannot start with 'Something'!"
)
You models seems to be correct, in order to be able to add mutiple choices in your template you need a formset. In addition you can put a formset and a form inside the same html form in a template and have them be validated individually. Each one only cares about the POST data relevant to them. Something like:
template.html
<form method="post" action="">
{% csrf_token %}
{{ choices_formset.management_form }} <!-- used by django to manage formsets -->
{{ question_form.as_p }}
{% for form in choices_formset %}
{{ form.as_p }}
{% endfor %}
<button type='submit'>Submit</button>
</form>
views.py
from django.db import IntegrityError, transaction
from django.shortcuts import redirect
from django.forms.formsets import formset_factory
from django.core.urlresolvers import reverse
def new_question(request):
ChoicesFormset = formset_factory(ChoicesForm)
if request.method == 'POST':
question_form = QuestionForm(request.POST)
choices_formset = ChoicesFormset(request.POST)
if question_form.is_valid():
question = Question(**question_form.cleaned_data)
if choices_formset.is_valid():
question.save()
new_choice_list = list()
append_choice = new_choice_list.append
for form in choices_formset:
form.cleaned_data.update({'question': question})
append_choice(Choice(**form.cleaned_data))
try:
with transaction.atomic():
Choice.objects.bulk_create(new_choice_list)
except IntegrityError as e:
raise IntegrityError
return redirect(reverse('question-detail-view', kwargs={'id': question.id}))
def question_detail(request, id):
question_list = Question.objects.get(id=id)
return render(request, 'question_detail.html', {'question_list': question_list})
urls.py
url(r'^question/$', new_question, name='new-question-view'),
url(r'^question/(?P<id>\d+)/$', question_detail, name='question-detail-view'),
If you want to use rather Ajax submission rather than django form sumbission check this tutoriel.

Can´t save images on Django Model after extending it

I´m having a really hard time with this. I have extended the Django user model. I created a separate app call "userprofile" (i have 2 apps: 'userprofile' and 'Administration') with new models.py:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
profile_image = models.ImageField(upload_to="/perfil/", blank=True, null=True)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user = u)[0])
the urls.py:
urlpatterns = patterns('',
url(r'^perfil/$', 'apps.userprofile.views.user_profile', name= 'perfil'),
)
and a views.py:
# Create your views here.
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from forms import UserProfileForm
from django.contrib.auth.decorators import login_required
#login_required
def user_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('../index')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance = profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('profile.html', args)
and of course a forms.py:
from django import forms
from models import UserProfile
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout
from crispy_forms.bootstrap import (FormActions, )
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
helper = FormHelper()
helper.form_method = 'POST'
helper.layout = Layout(
'profile_image',
FormActions(Submit('Editar', 'Editar', css_class= 'btn-primary'))
)
def save(self, commit=True):
fact = super(UserProfileForm, self).save(commit=False)
if commit:
fact.save()
return fact
So, what i´m trying to do is to let the user upload an image an let it use it as a profile image. I set the:
AUTH_PROFILE_MODULE = 'apps.userprofile.UserProfile' (the app is inside a folder call 'apps' that´s why the first 'apps' before userprofile)
in the settings.py, and i added the urls of 'userprofile' to the main project. Now I have the template where i can upload an image, the problem is that the image is never saved in the database so I can´t call a function to display the image in a template, let´s say the User Profile page.
Does anyone looking at the code knows what I am doing wrong?
According to the Django 1.7 docs, ModelForm classes must explicitly specify which fields should be updated when the save() method is called. Try adding fields = __all__ to your UserProfileForm class meta.
Relevant section of ModelForm documentation
Notice the first line of the Note in that link:
Any fields not included in a form by the above logic will not be set
by the form’s save() method.
I am still learning Django myself but that's what I would try first. :)

how to query between multi tables in django

See some snippets please:
1.Model UserProfile:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
email = models.EmailField(unique=True)
HEAD_CHOICE = (
('1', 'M'),
('2', 'F'),
)
image_id = models.CharField(max_length=2, choices=HEAD_CHOICE, default='2')
2.Model TimeLine:
from django.db import models
from django.contrib.auth.models import UserProfile
class TimeLine(models.Model):
user = models.ForeignKey(UserProfile)
3.TimeLine's views.py
from models import TimeLine
from django.shortcuts import render_to_response
def index(request):
timelinedict = TimeLine.objects.all()
return render_to_response('timeline.html', locals())
Question: how can I make the var 'timelinedict' contain fields (image_id, email) of UserProfile.
Thanks in advance:)
Question: how can I make the var 'timelinedict' contain fields
(image_id, email) of UserProfile.
It already does:
from models import TimeLine
from django.shortcuts import render
def index(request):
timelinedict = TimeLine.objects.all()
return render(request, 'timeline.html', {'objects': timelinedict})
In timeline.html:
{% for obj in objects %}
{{ obj.user.email }}
{{ obj.user.image_id }}
{% endfor %}
Use the render shortcut, not render_to_response. render will return the correct request context, which is useful when you are processing forms. It is best to get into the habit of using render.
Don't use locals(); because you will send every variable in the scope to your template. This is never what you want. Explicit is better than implicit.
You don't need to do anything special, you can directly access those attributes from instance of TimeLine.
For example
for t in TimeLine.objects.all():
print t.user.image_id, t.user.email
Similarly you can use that in template as well.
Your example for timelinedict isn't actually a dict it's a queryset containing TimeLine objects.
I think using the #property decorator as below will allow you to attach attributes to your TimeLine model objects.
class TimeLine(models.Model):
user = models.ForeignKey(UserProfile)
#property
def image_id(self):
return self.user.image_id
#property
def email(self):
return self.user.email
Of course, you could just access them directly in your template via object.user.image_id, etc.

Categories

Resources