I'm trying to create a simple WTForms-based admin interface for an SQLAlchemy app, using Jinja2 templates.
I've read the docs of WTForms-Alchemy and I understand that it can auto-generate a form from my model just via a few lines of code, like:
class UserForm(ModelForm):
class Meta:
model = User
My problem is that even though I have this form auto-generated, I found no resource anywhere about how can I make it into a functional HTML page. There are a few snippets about rendering errors for fields, as well as some SO answers mentioning macros for rendering whole fields, but I found absolutely no resource about how to generate a full, functional form automatically.
// I understand that this is something what Flask-Admin might do already, I'm not using Flask so this is not a possibility unfortunately.
WTForms leaves it up to you to figure out how to you want to render out your form after you pass it into your template. The simplest way to render a form would be to just iterate through your form and render the fields. When a field (or its label) is called, it emits HTML.
<form action="/some_url" method="POST">
{% for field in form %}
{{ field.label() }}
{{ field() }}
{% endfor %}
<button type="submit" />
</form>
The macros provided here provide an automated way to generate HTML surrounding these fields.
You can use wtf.quick_form like this, in which case you'll have a totally generic form template. Mark up your db.Model members with info{} properties to set field display names etc
<form method="post" action="/{{route}}">
<fieldset>
{{ wtf.quick_form(form, button_map={'submit':'success'}) }}
<input class="btn btn-success" type="submit" value="Submit" />
<button type="button" class="btn">Cancel</button>
</fieldset>
</form>
Your form definition:
class MyobjectForm(BaseModelForm):
class Meta:
model = Myobject
Then your route handler looks like this:
#app.route('/myobject', methods=('GET', 'POST'))
def myobject_route():
obj = Myobject()
form = MyobjectForm(obj = obj)
if form.validate_on_submit():
form.populate_obj(obj)
db.session.add(obj)
db.session.commit()
return redirect(url_for('index'))
return render_template('form.j2', form=form, title='My Object', route='myobject')
Related
What I really want to do is , if a user click on "ADD more" button then a same form repeat itself and the values should store in database, if he/she doesn't click of that button then only the values from first form should be stored.
I am not able to get this, I just created a form , and a table in database for those details but can't loop though the form neither in data.
please help.
This is the form and the button:
This is the model.py code:
from django.db import models
class experience(models.Model):
company_name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
startdate = models.Datefield(default = 01-01-2020)
lastdate = models.DateField(default = 01-01-2020)
profile = models.CharField(max_length=100)
description = models.TextField(max_length = 250)
This is the views.py code:
from django.shortcuts import render, redirect
import requests
from django.contrib.auth.models import User, auth
# Create your views here.
def profile(request):
return render(request, 'profile.html')
Unfortunately, there's no built-in way (as far as I know) in Django to do that without Javascript, but here's an approach:
HTML:
<div class="container" id="experiencesContainer">
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<button type="button" id="addMoreButton">Add more</button>
<button type="submit">Save Changes</button>
</div>
Django POST method:
# Get a list of submitted forms
experiences = request.POST.getlist('experienceForm')
for experience in experiences:
# this is how you loop throuh every form
experience.get('company_name)
Your javascript something like:
// clonning his childs as well
let cloneForm = document.querySelector('form[name=experienceForm]').cloneNode(true);
document.querySelector('div#experiencesContainer').appendChild(cloneForm);
// see this https://www.w3schools.com/jsref/met_node_clonenode.asp
Of course this code is not tested but I've done this in several projects before, hope it works!
A simple way would be to request the same view from the "Add", just make sure your form view saves the data when request method is POST.
<form action="{% url 'your-form-url' %}" method="GET">
{% csrf_token %}
<input type="submit" value="Add">
</form>
one other way to repeat forms would be using formsets. Formsets allow you to repeat the same form 'extra' times. Check out the documentation for more about this.
def repeat_form(request):
ExpFormSet = formset_factory(ExperienceForm, extra=3)
#extra defines the no. of forms you want to display
if request.method == 'POST':
formset = ExpFormSet(request.POST, request.FILES)
if formset.is_valid():
# do something with the formset.cleaned_data
#loop through each form in the formser
for form in formset.cleaned_data:
obj = form.save()
else:
formset = ExpFormSet()
return render(request, 'exp_form.html', {'formset': formset})
The corresponding template should be:-
<form method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
</form>
Make sure you add form.management_form. Using the combination of the above might solve your problem of taking and saving several inputs.
I am using Django and Postgresql as database. I have a HTML page with two fields name and item. I can save the data in the database by clicking on submit. But, I want to show the saved data from database in the HTML page. It means, whenever we load the page, it should show the existing saved data and after submitting new data, the list should be updated. Below is my python code.
models.py
from django.contrib.auth.models import User
from django.db import models
class AllocationPlan(models.Model):
name = models.CharField(max_length=50)
item = models.CharField(max_length=4096)
views.py
class HomePageView(TemplateView):
template_name = "index.html"
def post(self, request, **kwargs):
if request.method == 'POST':
form = AllocationPlanForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'index.html', { 'form': AllocationPlanForm() })
forms.py
from django import forms
from django.forms import ModelForm
from homeapp.models import AllocationPlan
class AllocationPlanForm(ModelForm):
class Meta:
model = AllocationPlan
fields = "__all__"
index.html
<html>
<form method="post">{% csrf_token %}
Name:<br>
<input type="text" name="name" >
<br>
Item:<br>
<input type="text" name="item" >
<br><br>
<input type="submit" value="Submit"/>
</form>
{% for i in form %}
{{ i.name }}
{{ i.item }}
{% endfor %}
</html>
It is returning NONE
Forms in Django are not used to display lists of data. It's merely used to render / validate the form (<form> tag in html). See also the forms doc.
Furthermore, it seems like you're using the TemplateView incorrectly. The post method in your view is only called on a POST request. When you're just viewing the page normally, the template is rendered normally, but since you only add the data to the template in a POST request, the template does not receive a form parameter when loading the view normally (therefore defaulting to None).
According to the TemplateView documentation, you can add the context like so:
class HomePageView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
# Get the allocation plans from database. Limit to last 10. Adjust to your own needs
context['plans'] = AllocationPlan.objects.all()[:10]
context['form'] = AllocationPlanForm()
return context
def post(self, request, **kwargs):
form = AllocationPlanForm(request.POST)
if form.is_valid():
form.save()
# Handle rest of request here (for example, return the updated page).
As you can see, there is no need to check if request.method == 'POST' in your post method, because Django only calls this method on POST requests. See also dispatch in the docs
To render the data from your database you can now access them in your template as plans:
{% for plan in plans %}
{{ plan.name }}
{{ plan.item }}
{% endfor %}
In your HTML, there is also no need to manually create the form content:
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
This will automatically create the HTML necessary for the form.
I am just beginner of Django.
At first, It seemed very convenient to use Django Form. I can get all which i already I defined in Model with ModelForm class. I can just put { form } in template. There are already pre-defined Field type and I can use it conveniently.
But it is tricky if you need to work with one who doesn't know Django or Python and are charge of making front-end part.
For example, I make template like this.
example.html
<form action="." method="post"> {% csrf_token %}
{{ form }}
<input type="submit" value="ok">
views.py
class TestView(FormView):
template_name = 'example.html'
form_class = TestForm
success_url = '/success/'
def form_valid(self, form):
print('data is validated! you can do work with cleaned data!')
return super(TestView, self).form_valid(form)
Q1 : A front-developer can't work with {{ form }}. right?
So I can change just example.html because it works properly if you set name attribute and type attribute correctly.
example.html
<form action="." method="post"> {% csrf_token %}
<input type="text" name="name">
<input type="text" name="calorie">
<input type="submit" value="Ok">
</form>
models.py
class Cereal(models.Model):
name = models.CharField(max_length=50)
calorie = models.CharField(max_length=50)
Q2 : I think it would be fine for a front developer to work with it if I change example.html above? right?
Well, if my questions are all fine, It seems fine to use ModelForm and write html code instead of {{ form }} django template syntax. But if you use ModelChoiceField, it seems nasty. You set queryset argument and it shows with select widget on template. for example queryset reqsult is { A, B, C }. it will turn to like below
<select id="id_something" name="name of something">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
As i told you earlier, I don't want to use Django template syntax like {{ form }} as much as I can. so I am going to write like below instead.
views.py
render(request, "example.html", {queryset : something.objects.all()})
example.html
<select id="id_something" name="name of something">
{% for element in queryset %}
<option>{{ element }}</option>
{% endfor %}
</select>
Q3: does this approach seem alright? Isn't it better way to use less Django template syntax when you use Django Form?
well, I concluded that using DRF(Django RestFramework) is the best way to separate tasks for front-end and back-end developers independently. But In current situation I am facing, Template(html, css, js) is already made. I need to change it to use with Django framework. Front end developer will change little bit with the Template which is already made.
When I watched some pycon video in Youtube, some people said that Djang framework is kind of too much coupled.
I hope I can get Idea more efficiently to use Django Model form with front-end developer.
Thanks for reading. My question could be ridiculous for an expert. Please ask me again if it is not clear.
What I need to do is access the same CreateView from two different pages. One of those two pages will send along some data that will be inserted into the CreateView form via get_initial(). The button I'm using to access the view is sending some POST data and so the CreateView is immediately thinking everything was successful and going all the way to get_success_url() and then rerouting. Below is what I have. I'm not sure the most elegant way to achieve what I wanted. I was trying to avoid any GET data (making the URLs messy) if possible? This there an elegant way to send POST data to the CreateView upon initialization?
template.html
<form method="POST" action="{% url 'purification:add' %}">
{% csrf_token %}
<input type="hidden" name="sample_id" value="{{ sample_data.id }}">
<button class="btn btn-success" id="add-purification-button" role="button" type="submit">+Add Purification</button>
</form>
view.py
class PurificationCreateView(LoginRequiredMixin, CreateView):
model = Purification
template_name = "add_purification.html"
fields = "__all__"
def get_initial(self):
if self.request.POST.get('sample_id'):
sample = Samples.objects.get(pk=self.request.POST['sample_id'])
return {'sample': sample}
return None
def get_success_url(self):
if "save-and-add-another" in self.request.POST:
return reverse('purification:add')
else:
return reverse('samples:oneSample', kwargs={'sample_id': self.request.POST['sample']})
I want to learn how can I add to template to my ModelForm i'm newbie. Below you can see my models.py, url.py and views.py:
My model.py looks like that:
from django.db import models
from django.forms import ModelForm
from django.contrib.auth.models import User
class Yazilar(models.Model):
yazi = models.CharField(max_length=200)
temsilci = models.ForeignKey(User)
class YaziForm(ModelForm):
class Meta:
model = Yazilar
My views.py function is below:
#login_required
def yazi_ekle(request):
yazim = YaziForm
return render_to_response('yazi/save.html', {'YaziForm': YaziForm})
My url.conf looks like below:
(r'^yazi/save/$', 'tryout.yazi.views.yazi_ekle'),
My question is about creating a form and what is that forms "action" parameter?
It seems to me that your problem is in the view, you should be doing something like this:
#login_required
def yazi_ekle(request):
yazim = YaziForm() # Look at the (), they are needed for instantiation
return render_to_response('yazi/save.html', {'YaziForm': yazim}) # Sending the form instance to the context, not the form class
Now, you have a variable named YaziForm in your template context. Django forms autorender to a bunch of table rows with the widgets as default, so in your file yazi/save.html, do this
<form method="post" action="">
{% csrf_token %}
<table>
{{YaziForm}}
</table>
<input type="submit" value="Submit Form"/>
</form>
That will render your form as a table automatically, though you have to add the logic for the form under POST.
You could in fact use <form action=""> since the url you want to post to is the same as the page you are on.
If you don't like that then as long as you have 'django.core.context_processors.request' in your TEMPLATE_CONTEXT_PROCESSORS in settings.py I think you could also do:
<form action="{{ request.path }}">
As always, see the docs :)
http://docs.djangoproject.com/en/1.1/ref/request-response/#django.http.HttpRequest.path
EDIT
In case, in the intervening year since this question was posted, the poster still hasn't tried to read the ModelForm docs... http://docs.djangoproject.com/en/1.2/topics/forms/modelforms/
Yes the view is wrong, you have instantiate the form. You also want some logic to handle the post data. If it's an edit view you probably also want the view to take an item id in the view args and have some logic to load that model instance.
eg:
#login_required
def yazi_ekle(request, id=None):
form_args = {}
if id is not None:
# edit an existing Yazilar
try:
yazilar = Yazilar.objects.get(pk=id)
except Yazilar.DoesNotExist:
return Http404('Yazilar not found')
form_args['instance'] = yazilar
# else create new Yazilar...
if request.POST:
form_args['data'] = request.POST
yazi_form = YaziForm(**form_args)
if yazi_form.is_valid():
yazilar = yazi_form.save(commit=True)
else:
yazi_form = YaziForm(**form_args)
return render_to_response('yazi/save.html',
{
'yazi_form': yazi_form
},
context_instance=RequestContext(request)
)
then in your urls.py something like:
(r'^yazi/ekle/(?P<id>\d+)?$', 'tryout.yazi.views.yazi_ekle'),
and in the template:
<form method="post" action="">
{% csrf_token %}<!-- required since Django 1.2 or later -->
<ul>
{{ yazi_form.as_ul }}
</ul>
<input type="submit" value="Submit Form"/>
</form>