I´m working with the Form Wizard which is alright but right now its not really pretty.
My Views.py is based on the docs:
class ContactWizard(SessionWizardView):
form_list = [DurationForm, ContactForm2]
def done(self, form_list, **kwargs):
return render(self.request, 'done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
My done.html contains:
{% extends "base.html" %}
{% block content %}
<h1> Done! </h1>
{% endblock %}
My forms are defined in forms.py:
from django import forms
class DurationForm(forms.Form):
Duration_in_secounds = forms.IntegerField()
class FrequencyForm(forms.Form):
Frequency_in_time_interval = forms.IntegerField()
Now i wonder how the forms even render because i never load them the way i used (for example with
{{form.as_p}}
Because whatever i change in my done.html doesnt affect the forms created with the wizard i have no idea how to add css to them.
Could any of you help me get whats going on here?
(sorry if the question is stupid/ already asked - its quite late here and i couldn´t find anything for the last two hours)
I think it doesn't matter it's a Form Wizard. You can just create a base class that you have DurationForm and ContactForm2 extend from. For example:
def __init__(self, *args, **kwargs):
super(BaseFormClass, self).__init__(*args, **kwargs)
# specific field
self.fields['name'].widget.attrs['class'] = 'my_class'
# all fields on form
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'my_class'
Not tested.
You should add load static to your template (html):
{% load staticfiles %}
<link href="{% static 'myapp/css/my.css' %}" rel="stylesheet">
This is where the css, scripts, etc. are loaded.
Related
I want to display data from 2 tables (and more in the future), but something doesnt work in my code.
my views.py:
**imports**
def home(request):
context = {'users': Person.object.all(),
'emails': Email.object.all()
}
return render(request,'app/home.html',context)
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
and in my home.html
{% extends "app/base.html" %}
{% block content %}
{% for user in users %}
Displaying user attributes works fine
{% endfor %}
Here should be emails
{% for email in emails %}
This displaying doesnt work
{% endfor %}
{% endbock content %}
So, displaying users works without any problem, but cant display anything form emails, but if I do it in shell, everything works well
A ListView [Django-doc] is designed to display only one queryset at a time. If you need to pass extra querysets, you can override the get_context_data(..) method [Django-doc]:
class PersonListView(ListView):
model = Person
template_name = 'app/home.html'
context_object_name = 'users'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(emails=Email.objects.all())
return context
Here we thus pass an extra variable emails to the template rendering engine. Note however that this queryset will not be paginated (or at least not without adding some pagination yourself).
my models.py:
Note that those are views, you need to write these in views.py.
I created three files:
2- view.py :
class AddTeamView(View):
def get (self, request):
form = TeamForm()
context = {'form': form}
return render(request, 'add_team.html', context)
1-forms.py:
class TeamForm(forms.Form):
name = forms.CharField( max_length='100')
details = forms.CharField(max_length='250')
3-add_team.html:
-here there is another file called "base.html"
{% extends 'base.html' %}
{% block title %}
add team
{% endblock %}
{% block content %}
<form action="/add_team/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% endblock %}
and i went to cmd and entered the server "python manage.py runserver"
it appeared on the browser:
"This page isn’t working
If the problem continues, contact the site owner.
HTTP ERROR 405"
A view can support methods like GET, POST, PUT, etc. given the corresponding method exists, so the view should have a .get(..), .post(..), .put(..), etc. function.
Here you only implemented a def get(self, request), and so POST requests are not allowed.
Based on the data you however show, this looks like the typical usecase of a CreateView [Django-doc]. The idea of these views is to encapsulate common scenario's such that by overriding a few attributes, one creates a view that is tailored towards a specific case, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = '/some/success_url'
The TeamForm should probably be a ModelForm, or at least a Form where you override the .save(..) function to properly save your data to the database, since right now, the form is not doing anything (well it receives the data, but after validation, it throws it away).
You might want to override the form_valid(..) function in case you do not want to redirect to the success_url. Furthermore it is very common that the success_url is resolved lazily from a given view name, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = reverse_lazy('view_name')
So, we don’t need to do a conditional to check if the request is a POST or if it’s a GET:
Your views.py:
from django.views.generic import View
class AddTeamView(View):
def post(self, request):
form = TeamForm(request.POST)
if form.is_valid():
new_tm = TeamModel(name=form.cleaned_data['name'], details=form.cleaned_data['details'])
new_tm.save()
return redirect('team_list')
return render(request, 'add_team.html', {'form': form})
def get(self, request):
form = TeamForm()
return render(request, 'add_team.html', {'form': form})
Hope this help you...
I wanted to ask if the following implementation is rational against Django rules of Views and Class Based views.
The scenario, I have implemented a mini administration section for users, not wanting to grant access to the general admin section of Django.
I am ok with the implementation and everything is working properly, recently I added also a small mixin to allow users to add related items the django admin way, the mixin is working properly (based on the original mixin of the Django admin interface). Just wanted to ask if this is a good approach or should I move to another implementation? (this is still a bit rough, made just for tests)
The mixin, we are actually checking against a query args, _popup, this is nearly the same way django admin checks for passed popups, this allows the form to load normally if working on it directly or as a popup when called through the parent form. partials/popup_reponse.html
class PopupMixin(object):
is_popup = False
popup_var = '_popup'
def get_context_data(self, **kwargs):
''' Add the _popup to the context, so we can propagade the templates'''
context = super(PopupMixin, self).get_context_data(**kwargs)
if self.popup_var in self.request.GET:
self.is_popup = True
context['is_popup'] = self.is_popup
return context
def form_valid(self, form):
if self.popup_var in self.request.POST:
self.is_popup = True
''' If this is a popup request, then we are working with a form
this means we save the form, and then override the default form_valid implementation
this allows us to inject newly created items in the form '''
if self.is_popup:
self.object = form.save()
return SimpleTemplateResponse('partials/popup_response.html', {
'value': self.object.id,
'obj': self.object
})
return super(PopupMixin, self).form_valid(form)
Then our create:
class TestCreateView(PopupMixin,CreateView):
form_class = TestForm
template_name = "add.html"
success_url = reverse_lazy('manager-list-tests')
This way in the view, I can make sure that first the base template does load only a bare form and not any other template parts, additionally I get to add a hidden field in the form:
{% if is_popup %}
<input type="hidden" name="_popup" value=1>
{% endif %}
Through this I override the default form_valid to respond with the same template as django admin uses:
<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<script type="text/javascript">
opener.dismissAddAnotherPopup(window, "{{ value }}", "{{ obj }}");
</script>
</body>
</html>
I'm using bootstrap3 as the default template pack in django_crispy_forms, and trying to render a form with the crispy tag:
{% crispy form %}
My form class has the following helper attributes:
class TheForm(forms.Form):
adv_var = forms.CharField(label="variable", max_length=70)
value = forms.FloatField()
def __init__(self, *args, **kwargs):
super(TheForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_class = 'form-inline'
self.helper.field_template = 'bootstrap3/layout/inline_field.html'
self.helper.layout = Layout(
'adv_var', 'value',
ButtonHolder(
Submit('submit', 'Start', css_class='button white')
)
)
When posting the form with errors, re-rendering the template does not show the errors even though I can print form._errors in the view and see the list of errors.
If I change the helper.field_template to another value (or remove it to set the default) the errors are displayed above each field - but I don't get the inline display anymore.
How can I use django-crispy-forms to display all errors of this form in a separate div for example?
We use django.contrib.messages to push a generic error string when the form has validation errors, and leave the field errors alone to render inline:
from django.contrib import messages
# ...
if not form.is_valid():
messages.error(request, "Please correct the errors below and resubmit.")
return render(request, template, context)
We then use bootstrap alerts to show all messages, including our generic error, though you could of course mark it up however you wanted.
But if all you want to do is move the errors into a separate block, add them to your request context:
from django.contrib import messages
# ...
if not form.is_valid():
context['form_errors'] = form.errors
return render(request, template, context)
and in your template:
{% crispy form %}
<div id='form-errors'>{{ form_errors }}</div>
You can then fiddle with the crispy form's helper attributes and styles to control the display of the inline errors.
Maybe the easier way is the next because it uses less imports...
.. in views:
if request.method == 'POST':
form = TheForm(request.POST)
if form.is_valid():
form.save()
return redirect('url_name')
else:
form = TheForm()
return render(request, 'form.html', {'form': form})
... and in the form you need only:
{% load crispy_forms_tags %}
{% crispy form %}
... where 'url_name' is defined name of pattern in urlpatterns (urls.py )... that's all you need really...
Crispy is a really smart system. The system knows how can intuitively to show the form errors.
You may want to consider using the FormView Generic View, especially if you are using crispy forms:
app/views.py:
from django.views.generic.edit import FormView
from django.urls import reverse_lazy
from .forms import MyForm
class MyFormView(FormView):
template_name = 'app/myform.html'
form_class = MyForm
success_url = reverse_lazy('success_url')
app/templates/app/myform.html:
{% load crispy_forms_tags %}
{% crispy form %}
I am a new Djangoer, and figuring out how to build custom widget, my problem is cannot get the MEDIA_URL in my widget's template, while the form use MySelectWidget able to get the MEDIA_URL itself.
#
#plus_sign.html
#
<a href="" class="" id="id_{{ field }}">
<img src="{{ MEDIA_URL }}images/plus_sign.gif" width="10" height="10" alt="Add"/>
</a>
^ cannot load the {{ MEDIA_URL}} to this widget's template, and therefore I can't load the .gif image properly. :(
#
#custom_widgets.py
#
from django import forms
class MySelectMultiple(forms.SelectMultiple):
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
plus = render_to_string("plus_sign.html", {'field': name})
return html+plus
#
#forms.py
#
from django import forms
from myapp.custom_widgets.py import MySelectMultiple
class MyForm(forms.ModelForm):
contacts = forms.ModelMultipleChoiceField(Contact.objects, required=False, widget=MySelectMultiple)
#
#views.py
#
def AddContacts(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
new = form.save()
return HttpResponseRedirect('/addedContact/')
else:
form = MyForm()
return render_to_response('shop/my_form.html', {'form': form}, context_instance=RequestContext(request))
#
#my_form.html
#
{% extends "base.html" %}
{% block content %}
{{ form.contacts }}
{% endblock %}
Please let me know how can I load the widget's image properly. Thank you so much for all responses.
Context processors only get applied when you use a RequestContext.
Your render method should be something like:
from django.template import RequestContext
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
context = RequestContext({'field': name})
plus = render_to_string("plus_sign.html", context)
return html + plus
And, as was mentioned by #czarchaic, make sure the media context processor is in TEMPLATE_CONTEXT_PROCESSORS (it should be by default).
Docs link.
Actually the correct way to do this is using Widget Media.
When defining your widget, you should define a Media inner class in which you should include a CSS file in order to style your widget. In this case make the <a> tag not to display text and have a plus sign background image.
class MyWidget(TexInput):
...
class Media:
css = {
'all': ('my_widget.css',)
}
If you really need to include the MEDIA_URL inside your rendered widget, I'd recommmend to import it directly from django.conf.settings and include settings.MEDIA_URL in your rendering context.
from django.conf import settings
class MyWidget(TextInput):
...
def render(self):
return render_to_string('my_widget.html', {
'MEDIA_URL': settings.MEDIA_URL,
...
})
Make sure the context processor is being loaded in settings.py
TEMPLATE_CONTEXT_PROCESSORS=(
...other processors,
"django.core.context_processors.media",
)
It is loaded by default if you don't specify TEMPLATE_CONTEXT_PROCESSORS, but if specified, the above processor must also be included.
http://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
I think we can do in this way, to pass the RequestContext, in order to access the MEDIA_URL without making another variable, and passing other variables at the 2nd parameter of the render_to_string method.
If we use:
context = RequestContext({'field': name})
The {{ field }} in the widget's template is empty and not able to access.
Here is the block which can access the MEDIA_URL as well as the {{ field }}.
However, I agree using the inner Media class for complex javascript and CSS setting. However, for a simple image src path, I think this will do.
def render(self, name, *args, **kwargs):
html = super(SelectMultipleWithModalDialog, self).render(name, *args, **kwargs)
**context = RequestContext({})
popup_plus = render_to_string("widgets/modal_dialog_plus_sign.html", {'field': name}, context_instance=context)**
return html + popup_plus
Please correct me if this is not the good way of doing it. Thanks for all participants of this thread.