I am building some forms using django-crispy_forms, I need them to be in the some specific order (specifically the order I stablished on the fields inside forms.py).
forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = {"razao_social","codigo_interno","nome_fantasia","endereco","bairro","uf","telefones","cnpj","fundacao",
"negocios","cidades_negocios","c1_nome","c1_whats","c1_email","c1_cargo","c1_comunicacao","c1_preferencia"}
views.py
from django.shortcuts import render
from .models import RelatorioVendas
from django.http import HttpResponse
from .forms import RegistroRelatorio
# Create your views here.
def novo_relatorio(request):
form = RegistroRelatorio(request.POST or None)
if form.is_valid():
form.save()
return HttpResponse('Ok...')
return render(request,'river/novo_relatorio.html',{'form':form})
html:
{% extends 'river/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h3>Novo Relatorio</h3>
<form method="post" >
{% csrf_token %}
{{form | crispy}}
<button type="submit">Ok</button>
</form>
{% endblock %}
I tried to use label_order inside this class, it worked, but when I use the crispy form it stops working...
I even tried to use the Layout method from the crispy library.
Use a 'list' instead of a 'set' for the fields in forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = ["razao_social", "codigo_interno", "nome_fantasia", "endereco", "bairro", "uf", "telefones", "cnpj",
"fundacao", "negocios", "cidades_negocios", "c1_nome", "c1_whats", "c1_email", "c1_cargo",
"c1_comunicacao", "c1_preferencia"]
According to the documentation:
A set is an unordered collection with no duplicate elements.
I don't see any problems here for crispy to render fields in the order that you provided. Show your html and views.
Related
I am new to Django framework and as a practice project I am trying to build an ecommerce website using Django. I have a class based view for my search page. I wrote the view to get the query for a particular query:
views.py
class SearchProductView(ListView):
template_name = "template.html"
queryset = Product.objects.filter(title__icontains='book')
print(queryset)
I would like to know how I can write a function to get the search queries dynamically. For e.g.: If i search book, then my queryset should contain all things about book and if I search car, then I should get all things about car.
template.html
{% extends "base.html" %}
{% block content %}
<div class='row'>
{% for object in object_list %}
<div class='col'>
{{ forloop.counter }}
{% include 'products/snippets/card.html' with instance=object %}
</div>
{% endfor %}
</div>
{% endblock %}
urls.py
from django.urls import path
from . import views
from .views import SearchProductView
app_name = 'search'
urlpatterns = [
path('', SearchProductView.as_view(), name='search_page'),
]
You need to define the get_queryset method, rather than the class-level queryset attribute. This can use your querystring parameters to filter the queryset dynamically.
You haven't shown your search form or said what your parameter is, but assuming it submits a GET parameter named q, you would do:
def get_queryset(self):
return Product.objects.filter(title__icontains=self.request.GET['q'])
Although the answer above will work I believe a better solution would be the following:
models.py
class Category(models.Model):
title = models.CharField(...)
class Product(models.Model):
...
category = models.ForeignKey(Category)
views.py
from django.db.models import Q
def get_queryset(self):
querystr = self.request.GET['q']
Product.objects.filter(
Q(title__icontains=querystr) | Q(category__title__icontains=querystr)
)
Using quick_form Flask-Bootstrap to generate the form is very convenient for me, however, I could not find in their documentation a way that I can add extras class to label as required by my template theme.
My forms.py class look like this:
from flask_wtf import Form
from wtforms import StringField,PasswordField,TextField,SubmitField
from wtforms.validators import InputRequired,EqualTo,Email,ValidationError
class Building_Form(Form):
BuildingName = TextField('Building Name')
BuildingType = TextField('Building Type')
FloorNums = TextField('Numers of Floor')
BuildUnits = TextField('Units in Building')
BuildSize = TextField('Building Size')
BuiltYear = TextField('Built in (year)')
BuildOpeningTime = TextField('Open Time')
BuildClosingTime = TextField('Close Time')
My routes.py is like following:
from app.import_core import *
from flask_wtf import FlaskForm
from wtforms import Form, BooleanField, StringField, PasswordField, validators
from wtforms.validators import DataRequired
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
from werkzeug.security import generate_password_hash, check_password_hash
from .forms import Building_Form
# from app.user.models import TBL_USER
import pdb
#app.route('/building')
def show_building_listing():
return render_template('building_listing.html')
#app.route('/builing/new',methods=['GET','POST'])
def add_new_builing():
form = Building_Form()
if request.method=='POST':
return '<h1>Ok Building added</h1>'
return render_template('new_building.html',form=form)
My new_building.html to render a form:
{% extends "__dashboard.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block include_css %}
<link rel="stylesheet" href="{{url_for('static',filename='css/login.css')}}">
<link rel="stylesheet" href="{{url_for('static',filename='css/bt-social-button/bootstrap-social.css')}}">
{% endblock %}
{% block content %}
<div class="card">
<div class="card-body">
{{ wtf.quick_form(form, extra_classes='bmd-label-floating') }}
</div>
</div>
{% endblock %}
I would like to add classes bmd-label-floating to my form label however it ended up with those classes in <form> instead.
How can I add that class my labels? Thanks.
I had a similar problem. A list of items was used to generate a form with multiple checkboxes. I needed to add a label class for styling them horizontally. I was able to loop through my form object and change class for the form field.label.
<form action="" method="post">
{% with test = form.example %}
{% for field in test %}
{{ field.label(class_='checkbox-inline') }}
{{ field }}
{% endfor %}
{% endwith %}
<p>{{ form.submit() }}</p>
</form>
Currently, I can leave a comment about a post, but I have to go to a separate comment create page. I want to include the comment form right under the post in the group detail page. I have tried to use {% include %}, but it cant seem to find the form. This is probabaly because I am trying to render the form on a different app's template than where the form.py and comment_form.html are (I create the form and the template for the form in the 'comments' app, and I'm trying to include the form in the the 'groups' app on the detail page. here are the relevant files.
comments/forms.py
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
comments/comment_form.html
<h2>this is the comment form</h2>
<form class="post-form" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
groups/views.detail :
def detail(request, group_id):
group = get_object_or_404(Group, pk= group_id)
posts = Post.objects.filter(group__id = group_id)
form = CommentForm()
return render(request, 'groups/detail.html', {'group': group, 'posts':posts, 'form':form})
groups/detail.html:
{% include form %}
this is the url that takes care of creating a comment (comments/urls.py):
from . import views
from django.urls import path
app_name = 'comments'
urlpatterns = [
path('<int:post_id>/create/', views.create, name='create'),
path('delete/<int:group_id>/<int:post_id>/<int:comment_id>', views.delete, name='delete'),
]
again, it can't find the template, probabaly because I have to make the 'groups' app aware of 'comment_form.html' 's existence. how do I accomplish this?
If you want to include comments/comment_form.html you should do:
{% include 'comments/comment_form.html' %}
Currently, you have {% include form %} which will use the variable form - that doesn't make sense, since form is a form instance CommentForm(), it is not a template name.
I'm trying to render a template that contains a DatePicker, but I'm getting a 500 error when I try. For my the code is correct, but it seems that something is failing or I'm not understanding correctly the way to do it.
The code is the following:
Reporting.py
from flask.ext.admin import BaseView, expose
from wtforms import DateField, Form
from wtforms.validators import Required
from flask.ext.admin.form import widgets
from flask import request
class DateRangeForm(Form):
start_date = DateField('Start', validators=[Required()], format = '%d/%m/%Y', description = 'Time that the event will occur', widget=widgets.DatePickerWidget)
class ReportingView(BaseView):
#expose('/')
def index(self):
form = DateRangeForm(request.form)
return self.render('reporting.j2', form=form)
Reporting template:
{% extends 'admin/master.html' %}
{% block body %}
{{super()}}
Working on it!
{% if form %}
{{form.start_date}}
{% endif %}
{% endblock %}
As davidism says in the comments, the default DateField just provides date parsing, it'll just be displayed as a normal text-input.
If you're ready to fully embrace html5 then you can use the DateField from wtforms.fields.html5 which will render a datepicker in any browser that supports it:
from flask import Flask, render_template
from flask_wtf import Form
from wtforms.fields.html5 import DateField
app = Flask(__name__)
app.secret_key = 'SHH!'
class ExampleForm(Form):
dt = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/', methods=['POST','GET'])
def hello_world():
form = ExampleForm()
if form.validate_on_submit():
return form.dt.data.strftime('%Y-%m-%d')
return render_template('example.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
The more usual approach is to find a nice datepicker online, that's using CSS and JS to handle it's display and include that code into your html template, and tell it to apply the datepicker style to any html element that has a class of datepicker. Then when you generate your form you can just do:
<!-- all your CSS and JS code, including the stuff -->
<!-- to handle the datepicker formatting -->
<form action="#" method="post">
{{ form.dt(class='datepicker') }}
{{ form.hidden_tag() }}
<input type="submit"/>
</form>
Any attributes (that aren't reserved for other use) you pass into rendering the form element will by default just add it as an attribute, for example the {{ form.dt(class='datepicker') }} will generate <input class="datepicker" id="dt" name="dt" type="text" value=""> so your CSS/JS can then do whatever it needs to do to provide a good interface for your user.
I feel like I must be missing something obvious but I am having a problem where my model formsets insist on retaining their data after submission. I am creating a page that allows a user to create a project and then add an arbitrary amount of materials to that project. JavaScript is taking care of dynamically adding new instances of the formset as needed. The code works fine the first time through, after that it "remembers" previous data. It happens for the material formset but not for the regular model form above it.
I'm thinking it must have something to do with the way I am creating my model formset. When the page is requested the view seems to be passing back the formset bound to old data rather than an unbound set. I'm new to Django and am trying to teach myself so there are probably things at work I do not quite grasp yet. Below is the code for the view:
def addproject_page(request):
# Define the formset to use to add the materials
MaterialFormSet = modelformset_factory(Material, exclude = ('project',))
# Check to see if there is some POST data from an attempt to fill out the form
if request.method == 'POST':
# Create a form for the project and for the material and use a prefix to separate the POST data for the two
project_form = ProjectForm(request.POST, prefix='project')
# Instantiate the formset to display multiple materials when creating a project
material_formset = MaterialFormSet(request.POST, prefix='material')
# Check both forms with the validators and if both are good process the data
if project_form.is_valid() and material_formset.is_valid():
# Save the data for the newly created project
created_project = project_form.save()
# Tell each material to be associated with the above created project
instances = material_formset.save(commit=False)
for instance in instances:
instance.project = created_project
instance.save()
# After the new project and its materials are created, go back to the main project page
return HttpResponseRedirect('/members/projects/')
# If there is no post data, create and show the blank forms
else:
project_form = ProjectForm(prefix='project')
material_formset = MaterialFormSet(prefix='material')
return render(request, 'goaltracker/addproject.html', {
'project_form': project_form,
'material_formset': material_formset,
})
Edit to add in my template code too in case it helps:
{% extends "base.html" %}
{% block external %}
<script src="{{ static_path }}js/projects.js" type="text/javascript"></script>
{% endblock %}
{% block title %}: Add Project{% endblock %}
{% block content %}
<h1>Add a Project</h1>
<form id="new_project_form" method="post" action="">
{{ project_form.as_p }}
<!-- The management form must be rendered first when iterating manually -->
<div>{{ material_formset.management_form }}</div>
<!-- Show the initial blank form(s) before offering the option to add more via JavaScript -->
{% for material_form in material_formset.forms %}
<div>{{ material_form.as_p }}</div>
{% endfor %}
<input type="button" value="Add Material" id="add_material">
<input type="button" value="Remove Material" id="remove_material">
<input type="submit" value="add" />
</form>
{% endblock %}
I think you need to use a custom queryset, so that your formset is instantiated with an empty queryset. You need to specify the queryset in the POST and GET branches of your if statement.
if request.method == "POST":
...
material_formset = MaterialFormSet(request.POST, prefix='material', queryset=Material.objects.none())
...
else:
material_formset = MaterialFormSet(prefix='material', queryset=Material.objects.none())
At the moment, your formset is using the default queryset, which contains all objects in the model.
Answer of the question the old data is always persists in modelformset is here. https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#changing-the-queryset as it is given in the docs chenge the queryset by overriding the constructor of the basemodelformset
from django.forms.models import BaseModelFormSet
from myapp.models import Author
class CalendarFormset(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(CalendarFormset, self).__init__(*args, **kwargs)
self.queryset = Calendar.objects.none()
A same problem was discussed here
django modelformset_factory sustains the previously submitted data even after successfully created the objects