Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lesson 16 - Form Rendering, Paging, and ModelForms

Lesson 16 - Form Rendering, Paging, and ModelForms

Dana Spiegel

December 05, 2012
Tweet

More Decks by Dana Spiegel

Other Decks in Technology

Transcript

  1. Quick Review • Changing the widget used by a FormField

    • Form Processing • Validation • Cleaning • Mandrill API for sending email 2
  2. Final Fix for Port Issue! • Yes, its fixed •

    But a few changes are needed: • Access your web app at: http://dev.hatcherydevshop.com/ USERNAME/ • When logging in via ssh, the above URL will be shown as a reminder • runserver works the same way, but isn’t accessible via the older http:// HOST:PORT URL • Your web app runs in a sub-path of the site, but your web app doesn’t see that! • All of this is handled by nginx 3
  3. Form Rendering in Templates • To render a form in

    a template: • Provide the <form> tag • Include CSRF protection • Include any submit or cancel buttons • Django provides built-in generic form rendering 4 <form class="form-horizontal" action="" method="post"> {% csrf_token %} <button type="submit">Send Message</button> {{ form.as_p }} {{ form.as_table }} {{ form.as_ul }}
  4. Form Rendering in Templates • Forms rendering can be customized

    as well (useful for when a CSS template framework is used, like Bootstrap • Use an include to create a custom, reusable form rendering template • Ensure all form parts are rendered: • CSRF (but be careful if multiple Django forms are rendered in one HTML form) • Hidden fields • Form errors (non_field_errors) • Iterate over each field and render: • Label • Whether the field is required or not • Field • Field errors 5
  5. Example: Form Template 6 {% csrf_token %} {% for hidden

    in form.hidden_fields %}{{ hidden }}{% endfor %} {% if form.non_field_errors %} <ol> {% for error in form.non_field_errors %} <li><strong>{{ error|escape }}</strong></li> {% endfor %} </ol> {% endif %} {% for field in form %} <div class="control-group {% if field.errors %}error{% endif %}"> <label class="control-label {{ field.css_classes }}" for="{{ field.auto_id }}"> {{ field.label }}{% if field.field.required %}*{% endif %} </label> <div class="controls"> {{ field }} <span class="help-inline"> {% for error in field.errors %} {{ error|escape }} {% endfor %} </span> </div> </div> {% endfor %}
  6. Representing Location in the Navbar 7 • Bootstrap provides a

    way to represent the current location in a web app by highlighting the user’s location in the navigation bar • li items in the navbar can be highlighted using the “active” css class • To enable this functionality, navbar li elements must determine if they are “active” based on the current path • Create template tag that returns the text “active” and include this template tag in the li class for each navbar item in base.html • active template tag is a custom template tag in softball_tags.py • Takes request, to be able to example current path and a search string indicating which path is represented by the navbar item {% load softball_tags %} <li class="{% active request '/team/' %}"> <a href="{% url team_list %}">Teams</a></li> <li class="{% active request '/player/' %}"> <a href="{% url player_list %}">Players</a></li> <li class="{% active request '/game/' %}"> <a href="{% url game_list %}">Games</a></li>
  7. Representing Location in the Navbar 8 • Custom template tag

    lives in softball/templatetags/softball_tags.py • Method takes request and search pattern, then uses pattern as regular expression to search request.path, returning the text “active” • Custom template tags are discovered automatically by Django • Make sure that 'django.core.context_processors.request' is included in TEMPLATE_CONTEXT_PROCESSORS in settings.py import re from django import template register = template.Library() @register.simple_tag def active(request, pattern): if re.search(pattern, request.path): return 'active' return ''
  8. Adding Paging to Lists: Views • Django provides a built

    in Paginator module to support paging long lists • Works by wrapping a list query in a Paginator object and using a page URL argument to indicate the current page • Ensure Paginator exceptions are caught and handled appropriately 9 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger def team_list(request): paginator = Paginator( models.Team.objects.all().order_by('name'), 10) page = request.GET.get('page') try: teams = paginator.page(page) except PageNotAnInteger: teams = paginator.page(1) except EmptyPage: teams = paginator.page(paginator.num_pages) return TemplateResponse(request, 'softball/team/list.html', { 'teams': teams, })
  9. Adding Paging to Lists: Template • In the template, use

    the Paginator.paginator object to determine: • # of pages • Current page # • Iterate over available pages to generate page links 10 <div class="span4"> <div class="pagination pagination-left pagination-small"> <a class="btn" href="{% url team_create %}">Add Team</a> </div> </div> <div class="span8"> <div class="pagination pagination-right pagination-small"> <ul> {% if teams.paginator.num_pages > 1 %} <li><span>{{ teams.paginator.count }} Teams Total</span></li> {% if teams.has_previous %} <li><a href="?page={{ teams.previous_page_number }}">&laquo;</a></li> {% else %} <li class="disabled"><span>&laquo;</span></li> {% endif %} {% for page_num in teams.paginator.page_range %} <li {% if page_num == teams.number %}class="active"{% endif %}> <a href="?page={{ page_num }}">{{ page_num }}</a> </li> {% endfor %} {% if teams.has_next %} <li><a href="?page={{ teams.next_page_number }}">&raquo;</a> </li> {% else %} <li class="disabled"><span>&raquo;</span></li> {% endif %} {% endif %} </ul> </div> </div>
  10. Django ModelForms • Django ModelForms are an extension of Django

    Forms • Enable the creation of forms that allow user manipulation of Model-based data • Inherit from ModelForm class • Specify model in Meta • Meta is similar to Model’s Meta class that customizes ModelForm instance • Fields are automatically created based on the Model’s fields • ForeignKeys are represented by ModelChoiceField, which is a type of ChoiceField • ManyToManyFields are represented by MultipleModelChoiceField, which is a type of MultipleChoiceField • In both cases, choices are generated by a queryset of related Models 11
  11. Django ModelForms (cont’d.) • FormField inherits settings from ModelField •

    required is set automatically based on ModelField’s blank setting • label is set automatically based on ModelField’s verbose setting • choices are set automatically based on ModelField’s choices setting • is_valid() works just like in a regular Form, but also performs Model validation • When instantiating a ModelForm, include an instance if it exists to update that instance with form data • If not specified, a new Model instance is created automatically • save() is a special ModelForm method that will create or update a Model instance based on request.POST data • If commit is not specified, save() will also create/update the instance in the database • If commit=False is specified, Model instance is created or updated, but not saved to database and must be save()’d explicitly 12
  12. Example: ModelForm 13 from django.forms import ModelForm import models class

    TeamForm(ModelForm): error_css_class = 'text-error' required_css_class = 'text-required' class Meta: model = models.Team class PlayerForm(ModelForm): error_css_class = 'text-error' required_css_class = 'text-required' class Meta: model = models.Player class GameForm(ModelForm): error_css_class = 'text-error' required_css_class = 'text-required' class Meta: model = models.Game fields = ('played_on', 'location', )
  13. Configuring a ModelForm • To use only a subset of

    Model Fields in a ModelForm, specify either fields or exclude collections in Meta; order matters! • Override field widgets using widgets dict 14 class PartialAuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title') class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ('birth_date',) from django.forms import ModelForm, Textarea class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') widgets = { 'name': Textarea(attrs={'cols': 80, 'rows': 20}), }
  14. Handling ModelForms in Views • Works just like regular Forms

    • Must have “create” and “update” views • Create view should assume no existing instance is specified and should render an empty ModelForm • Upon successful creation, redirect to the instance view 15 def team_create(request): if request.method == 'POST': team_form = forms.TeamForm(request.POST) if team_form.is_valid(): team = team_form.save() return redirect('team_view', team_id=team.id) else: team_form = forms.TeamForm() return TemplateResponse(request, 'softball/team/create.html', { 'team_form': team_form, })
  15. Handling ModelForms in Views • Update view should expect an

    existing instance specified (or raise an Http404 exception) and should include the specified instance in the ModelForm • Pass the instance into the form for rendering (not necessary, but easy) 16 def team_edit(request, team_id=None): try: team = models.Team.objects.get(pk=team_id) except models.Team.DoesNotExist: raise Http404 if request.method == 'POST': team_form = forms.TeamForm(request.POST, instance=team) if team_form.is_valid(): team = team_form.save() return redirect('team_view', team_id=team.id) else: team_form = forms.TeamForm(instance=team) return TemplateResponse(request, 'softball/team/edit.html', { 'team': team, 'record': team.record(), 'team_form': team_form, })
  16. ModelForms in Templates: Create 17 {% extends 'softball/base.html' %} {%

    block title %}Add Team{% endblock %} {% block body %} <div class="page-header"> <h1>Add Team</h1> </div> <form action="" method="post" class="form-horizontal"> <div class="row-fluid"><div class="span12"> {% with team_form as form %} {% include "softball/include_form.html" %} {% endwith %} <div class="form-actions"> <button type="submit" class="btn btn-primary"> Add Team</button> <a class="btn" href="{% url team_list %}">Cancel</a> </div> </div></div> </form> {% endblock %}
  17. ModelForms in Templates: Edit 18 {% extends 'softball/base.html' %} {%

    block title %}Edit Team: {{ team.name }}{% endblock %} {% block body %} <div class="page-header"><h1>Edit Team</h1></div> <form action="" method="post" class="form-horizontal"> <div class="row-fluid"><div class="span12"> {% with team_form as form %} {% include "softball/include_form.html" %} {% endwith %} <div class="form-actions"> <button type="submit" class="btn btn-primary"> Update Team</button> <a class="btn" href=" {% url team_view team_id=team.id %}">Cancel</a> </div> </div></div> </form> <div class="row-fluid"> <div class="span12"> <h3>Players</h3> {% include 'softball/team/part.player_list.html' %} </div> </div> <div class="row-fluid"> <div class="span12"> <h3>Games</h3> {% include 'softball/team/part.game_list.html' %} </div> </div> {% endblock %}
  18. Edit and Create Views in urls.py 19 url(r'^team/create/$', 'softball.views.team_create', name='team_create'),

    url(r'^team/(?P<team_id>\d+)/edit/$', 'softball.views.team_edit', name='team_edit'),
  19. In Class 20 • Add create and edit pages for

    Team, Player, Game • Add Team, Player, Game forms • Link up create and edit functionality to view pages