Dynamically Add or Remove WTForms validators in Flask - flask

Is there a way to do this?
Below is an example of what I'm trying to do:
class TestForm(FlaskForm):
email = EmailField('Email', validators=[InputRequired('Email is required.')])
start = SubmitField()
Then in a route:
del form.email.validators
# I also tried:
form.email.validators.remove
Basically I want to use stored data to determine if the field should be required or not for a predefined form.

Dynamically create an internal subclasses of the form within your view. Remove any validators from the fields of the internal subclass and then instance a form from the internal subclass. In code, something like:
Define a form, the first_name field has two validators.
class TestForm(FlaskForm):
first_name = StringField(validators=[InputRequired(), Length(8)])
submit = SubmitField()
In your view:
# Dynamic subclass
class F(TestForm):
pass
# Remove the length validator.
# Validators are in a dict called kwargs
validators = F.first_name.kwargs.get('validators')
for validator in validators:
if isinstance(validator, Length):
validators.remove(validator)
# instance a form
_form = F()
# use the form ....
Single file example app.py:
from flask import Flask, render_template_string
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import InputRequired, Length
app = Flask(__name__)
app.config['SECRET_KEY'] = '13332311ecd738748f27a992b6189d3f5f30852345a1d5261e3e9d5a96722fb9'
class TestForm(FlaskForm):
first_name = StringField(validators=[InputRequired(), Length(8)])
submit = SubmitField()
html_template = '''
{% if form.first_name.errors %}
<ul class="errors">
{% for error in form.first_name.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form role="form" method="post" action="" name="Form1">
{{ form.hidden_tag() }}
{{ form.first_name }}
{{ form.submit() }}
</form>
'''
success_template = '''
<h1>Success</h1>
'''
#app.route('/', methods=['GET', 'POST'])
def index():
# Dynamic subclass
class F(TestForm):
pass
# Remove the length validator.
# Validators are in a dict called kwargs
validators = F.first_name.kwargs.get('validators')
for validator in validators:
if isinstance(validator, Length):
validators.remove(validator)
# instance a form
_form = F()
# print the validators
for field in _form:
print(f"Field: {field.name} has {len(field.validators)} validator(s)")
for validator in field.validators:
print(validator)
if _form.validate_on_submit():
print('Validation passed')
return render_template_string(success_template)
return render_template_string(html_template, form=_form)
if __name__ == '__main__':
app.run()

You can try:
form.email.validators = ()
This worked well for me.
You can also add back the validators with:
form.email.validators = [InputRequired('Email is required')]

Related

Get data from django database

So,how get data from django database?I have django models and created 2 objects there.I want to get 2 objects and write in html file. admin panel: https://i.stack.imgur.com/7WUZr.png
view.py
def vds(request):
HTML_STRING = render_to_string("vds.html", context=context1)
return HttpResponse(HTML_STRING)
VDSTARIFS_obj = VDSTARIFS.objects.get(id=1)
context1 = {
"prise": VDSTARIFS_obj.prise,
}
file with models
class VDSTARIFS( models.Model):
prise = models.CharField(max_length= 10,)
def __str__(self):
return str(self.prise)```
Referring to the Django docs:
https://docs.djangoproject.com/en/3.2/intro/tutorial03/#a-shortcut-render
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
Edited for your model
from django.shortcuts import render
from .models import VDSTARIFS
def vds(request):
ALL_VDSTARIFS = VDSTARIFS.objects.all()
FIRST_VDSTARIF = ALL_VDSTARIFS[:1]
# Get prise
prise = FIRST_VDSTARIF.prise
# Alternatives to print 'prise' in template
# Pass only the first item of vds list
context = {
"FIRST_VDSTARIF": FIRST_VDSTARIF
}
# Pass prise only
context = {
"prise": prise
}
# All records in html also the needed one in separated item
context = {
"ALL_VDSTARIFS": VDSTARIFS,
"FIRST_VDSTARIF": FIRST_VDSTARIF
}
return render(request, 'vds.html', context)
}
In template you can use if
https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#if
{# Runs along all tarifs and print only one, not good. #}
{% for vdstarif in ALL_VDSTARIFS %}
{% if vdstarifs.id == "1" %}
{{ vdstarif.prise }}
{% endif %}
{% endfor %}
{# Only print the needed one #}
{{ FIRST_VDSTARIF.prise }}
{# Print prise #}
{{ prise }}
You might follow python and django coding conventions and my suggestions.
Rename VDSTARIFS -> VdsTarif (look at your admin page, Django adds 's' to your model to make it plural)
Use singular names for your models and add a suffix to variable names in views like vdstarif_list.
For "prise" field use DecimalField, looks like it is a CharField. Is it prise or price?
https://docs.djangoproject.com/en/3.2/ref/models/fields/#decimalfield
from django.shortcuts import render
from .models import VdsTarif
def vds(request):
vdstarif_list = VdsTarif.objects.all()
context = {
"vdstarif_list" : vfstarif_list[:0].prise
}
return render(request, 'vds.html', context)

Flask-WTF Passes Validation when it Should Fail

I have a Flask App that passes user input validation when it should fail. I have similar code in another part of the app that works just fine. It seems like the FileAllowed() method is not being called. Or if it is, it's returning true.
This code uploads a user file to s3.
The MultipleFileField() method has a validation check for only image file extensions. However, any file passes this check. The InputRequired() method works just fine.
I've tried multiple variations of this and nothing has worked. It's not a CRSF issue because other routes with similar code work without it.
flask_wtf Form:
class AddImgForm(FlaskForm): # should use InputRequired() not DataRequired()
images= MultipleFileField('Upload Images', validators=[InputRequired(),FileAllowed(['jpg', 'png', 'jpeg', 'tif'])])
submitBTN2 = SubmitField('Upload')
Route:
#users.route("/account", methods=['GET', 'POST'])
#login_required
def account():
form = UpdateAccountForm()
if form.validate_on_submit():
if form.picture.data: # if a picture is provided save picture
picture_file= save_picture(form.picture.data, 'p') # saves picture and returns dict with ['filepath'] and ['filename']
BUCKET= os.environ['BUCKET'] # should send to 'bucket-publicaccess/uploads' bucket in production
s3= boto3.resource("s3",
region_name = "us-east-2", # had to add "us-east-2" as incorrect region was generated
config= boto3.session.Config(signature_version='s3v4'), # must add this to address newer security
aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"],
aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"]) # AWS Generated key pairs
s3.Bucket(BUCKET).upload_file(picture_file['filepath'], 'uploads/'+ picture_file['filename']) #upload to s3
current_user.image_file= 'uploads/'+picture_file['filename']
print(current_user.image_file)
os.remove(picture_file['filepath']) # remove file from tmp directory
current_user.username = form.username.data
current_user.email = form.email.data
db.session.commit() # commit changes
flash('Your account has been updated!', 'success')
return redirect(url_for('users.account'))
elif request.method == 'GET':
form.username.data = current_user.username
form.email.data = current_user.email
image_file = current_user.image_file
return render_template('account.html', title='Account',
image_file=image_file, form=form)
HTML:
<form method="POST" action="" enctype="multipart/form-data" id="addImgForm">
{{ addImgForm.hidden_tag() }}
<fieldset class="form-group">
<div class="form-group">
{{ addImgForm.images.label() }}
{{ addImgForm.images(class="form-control-file") }}
{% if addImgForm.images.errors %}
{% for error in addImgForm.images.errors %}
<span class="text-danger">{{ error }}</span></br>
{% endfor %}
{% endif %}
</div>
<div class="form-group">
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
{{ addImgForm.submitBTN2(class="btn btn-outline-info") }}
</div>
</fieldset>
</form>
Any help would be appreciated as most questions are about this failing while this code always passes.
The problem is with the FileAllowed validator, it is expecting a single instance of FileStorage when validating, whereas MultipleFileField passes a list of FileStorage instances to the validator. You can overcome this by writing your own validator, for example:
class MultiFileAllowed(object):
def __init__(self, upload_set, message=None):
self.upload_set = upload_set
self.message = message
def __call__(self, form, field):
# FileAllowed only expects a single instance of FileStorage
# if not (isinstance(field.data, FileStorage) and field.data):
# return
# Check that all the items in field.data are FileStorage items
if not (all(isinstance(item, FileStorage) for item in field.data) and field.data):
return
for data in field.data:
filename = data.filename.lower()
if isinstance(self.upload_set, Iterable):
if any(filename.endswith('.' + x) for x in self.upload_set):
return
raise StopValidation(self.message or field.gettext(
'File does not have an approved extension: {extensions}'
).format(extensions=', '.join(self.upload_set)))
if not self.upload_set.file_allowed(field.data, filename):
raise StopValidation(self.message or field.gettext(
'File does not have an approved extension.'
))
A simple single file example using Flask, Flask-WTF and Flask-Boostrap:
from collections import Iterable
from flask_bootstrap import Bootstrap
from flask import Flask, redirect, url_for, render_template_string
from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed
from markupsafe import Markup
from werkzeug.datastructures import FileStorage
from wtforms.fields import MultipleFileField, SubmitField
from wtforms.validators import InputRequired, StopValidation
app = Flask(__name__)
app.config['SECRET_KEY'] = '123456790'
Bootstrap(app)
class MultiFileAllowed(object):
def __init__(self, upload_set, message=None):
self.upload_set = upload_set
self.message = message
def __call__(self, form, field):
if not (all(isinstance(item, FileStorage) for item in field.data) and field.data):
return
for data in field.data:
filename = data.filename.lower()
if isinstance(self.upload_set, Iterable):
if any(filename.endswith('.' + x) for x in self.upload_set):
return
raise StopValidation(self.message or field.gettext(
'File does not have an approved extension: {extensions}'
).format(extensions=', '.join(self.upload_set)))
if not self.upload_set.file_allowed(field.data, filename):
raise StopValidation(self.message or field.gettext(
'File does not have an approved extension.'
))
class ImagesForm(FlaskForm):
images = MultipleFileField(
'Upload Images',
validators=[
InputRequired(),
MultiFileAllowed(['jpg', 'png', 'jpeg', 'tif'])
]
)
submit = SubmitField('Upload')
upload_template = '''
{% import "bootstrap/wtf.html" as wtf %}
<form method="POST" enctype="multipart/form-data">
{{ wtf.quick_form(form) }}
</form>
'''
#app.route('/')
def index():
return Markup("<a href='uploads'>Go to the uploads<a>")
#app.route('/uploads', methods=['GET', 'POST'])
def upload():
form = ImagesForm()
if form.validate_on_submit():
if form.images:
for image in form.images.data:
print 'Uploaded File: {}'.format(image.filename)
return redirect(url_for('index'))
else:
print form.errors
return render_template_string(upload_template, form=form)
if __name__ == '__main__':
app.run()
Thanks for the above! I had to change to
class MultiFileAllowed(object):
def __init__(self, upload_set, message=None):
self.upload_set = upload_set
self.message = message
def __call__(self, form, field):
if not (all(isinstance(item, FileStorage) for item in field.data) and field.data):
return
for data in field.data:
filename = data.filename.lower()
if isinstance(self.upload_set, Iterable):
print(filename, flush=True)
print(any(filename.endswith("." + x) for x in self.upload_set), flush=True)
if not any(filename.endswith("." + x) for x in self.upload_set):
raise StopValidation(
self.message
or field.gettext("File does not have an approved extension: {extensions}").format(
extensions=", ".join(self.upload_set)
)
)

How to use django smart-selects with modelform?

I'm using smart-selects.
Edited : Here is the code that works for me, after adding {{ form.media.js }} in the template , thanks to #Evangelos
models.py :
from django.db import models
from smart_selects.db_fields import ChainedForeignKey, ChainedManyToManyField
class ChoixTangente(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class ChoixModele(models.Model):
name = models.CharField(max_length=255)
tangentes = models.ManyToManyField('Choixtangente', blank=True)
def __str__(self):
return self.name
class TypeModele(models.Model):
tangente = models.ForeignKey(ChoixTangente, blank=True, null=True)
type_modele = ChainedForeignKey(
ChoixModele,
chained_field="tangente",
chained_model_field="tangentes",
show_all=False,
auto_choose=True,
blank=True, null=True
)
def __unicode__(self):
return str(self.pk)
form.py :
from django import forms
from .models import ChoixTangente, ChoixModele, TypeModele
class TypeModeleForm(forms.ModelForm):
class Meta:
model = TypeModele
fields = ('tangente', 'type_modele')
views.py
from .models import ChoixTangente, ChoixModele, TypeModele
from .forms import TypeModeleForm
def type_modele_new(request):
if request.method == "POST":
form = TypeModeleForm(request.POST)
if form.is_valid():
modele_instance = form.save()
return redirect('calculs.views.type_modele_detail', pk=modele_instance.pk)
else:
form = TypeModeleForm()
return render(request, 'calculs/type_modele_new.html', {'form': form})
def type_modele_detail(request, pk):
modele_instance = get_object_or_404(TypeModele, pk=pk)
return render(request, 'calculs/type_modele_detail.html', {'modele_instance': modele_instance})
template : type_modele_new.html
{% load staticfiles %}
<form method="POST">
{% csrf_token %}
**{{ form.media.js }}**
{{ form.as_p}}
<input type="submit" value="Submit">
</form>
urls.py
from django.conf.urls import url, include
from . import views
from smart_selects import urls as smart_selects_urls
urlpatterns = [
url(r'^chaining/', include('smart_selects.urls')),
url(r'^type_modele/new/$', views.type_modele_new, name='modele_new'),
url(r'^type_modele/(?P<pk>[0-9]+)/$', views.type_modele_detail, name='modele_detail'),
]
Edited : and here was the problem :
I have it working fine in admin :
I create ChoixTangente instances
I create ChoixModele
instances and select ChoixTangentes instances in the list
I can create TypeModele" instances by selecting "tangente" in a list of ChoixTangente and "type_modele" in the list of corresponding ChoixModele choices resulting from the steps 1) and 2)
see Admin form screenshot
I whish to have my users do the same through a form. But I can't have it working.
the field "Tangente" is populated with the list of ChoixTangente but when I choose a value the field "type_modele" stays empty instead of displaying a list of corresponding choices.
see form screenshot for the users
First of all where is your views?
Try to post more complete question with files for better understanding.
Where is the model Appareil in the models.py you are refering to in forms.py?
And your fields should be in list [].
Your forms.py should look like:
from django import forms
from .models import Modele
class AppareilForm(forms.ModelForm):
class Meta:
model = Modele
fields =['tangente', 'modele']
You don't need to write javascript.
Just remember to include url(r'^chaining/', include('smart_selects.urls')) to your urls.py
in your view call the AppareilForm Read about forms in views for details.
And in the template just use {{form.as_p}} and {{ form.media.js }} before to load javascript and make sure to load static in your template. {% load static % }
Your template should look something like this
{% load static % }
<form action="your url that points to your view" method="POST">
{% csrf_token %}
{{ form.media.js }}
{{ form.as_p}}
<input type="submit" value="Submit">
</form>
Read the django-selects description carefully.

Django: Add & delete extra input field using class view at a click of a button

What I am trying to do is the following: User will have a production (also known as podcast episode) already created with the necessary info until this point (production_id would be the id for this query). The idea is, when user arrives to ChapterMark template, he would be able to create several timestamps to point out certain topics he/she is talking throughout his/her episode. chaptermark_id is created since it would be a One-To-Many and with this id I can add as much timestamps as I want within that episode. With this in mind, which is the best approach for this type of situation and how I can implement it in my form, class view and template?
Thanks in advance
Here is my views.py:
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404
from django.views.generic import View, RedirectView, TemplateView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms.client_setup import ClientSetupForm
from .forms.podcast_setup import PodcastSetupForm
from .forms.episode_info import EpisodeInfoForm
from .forms.image_files import EpisodeImageFilesForm
from .forms.wordpress_info import EpisodeWordpressInfoForm
from .forms.chapter_marks import EpisodeChapterMarksForm
from .forms.show_links import ShowLinksForm
from .forms.tweetables import TweetablesForm
from .forms.clicktotweet import ClickToTweetForm
from .forms.schedule import ScheduleForm
from .forms.wordpress_account import WordpressAccountForm
from .forms.wordpress_account_setup import WordpressAccountSetupForm
from .forms.wordpress_account_sortable import WordpressAccountSortableForm
from .forms.soundcloud_account import SoundcloudAccountForm
from .forms.twitter_account import TwitterAccountForm
from producer.helpers import get_podfunnel_client_and_podcast_for_user
from producer.helpers.soundcloud_api import SoundcloudAPI
from producer.helpers.twitter import TwitterAPI
from django.conf import settings
from producer.models import Client, Production, ChapterMark, ProductionLink, ProductionTweet, Podcast, WordpressConfig, Credentials, WordPressSortableSection, \
TwitterConfig, SoundcloudConfig
from django.core.urlresolvers import reverse
from producer.tasks.auphonic import update_or_create_preset_for_podcast
class EpisodeChapterMarksView(LoginRequiredMixin, View):
form_class = EpisodeChapterMarksForm
template_name = 'fc/forms_chapter_marks.html'
def get(self, request, *args, **kwargs):
initial_values = {}
user = request.user
# Lets get client and podcast for the user already. if not existent raise 404
client, podcast = get_fc_client_and_podcast_for_user(user)
if client is None or podcast is None:
raise Http404
# The production_id or the chaptermark_id must be passed on teh KWargs
production_id = kwargs.get('production_id', None)
chaptermark_id = kwargs.get('chaptermark_id', None)
if chaptermark_id:
chaptermark = get_object_or_404(ChapterMark, id=chaptermark_id)
production = chaptermark.production
elif production_id:
production = get_object_or_404(Production, id=production_id)
chaptermark = None
initial_values['production_id'] = production.id
if chaptermark is not None:
initial_values['chaptermark_id'] = chaptermark_id
initial_values['start_time'] = chaptermark.start_time
initial_values['title'] = chaptermark.title
form = self.form_class(initial=initial_values)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# lets get the data
production_id = form.cleaned_data.get('production_id')
chaptermark_id = form.cleaned_data.get('chaptermark_id')
start_time = form.cleaned_data.get('start_time')
title = form.cleaned_data.get('title')
# Get production
production = get_object_or_404(Production, id=production_id)
# if a chaptermark existed, we update, if not we create
if chaptermark_id is not None:
chaptermark = ChapterMark.objects.get(id=chaptermark_id)
else:
chaptermark = ChapterMark()
chaptermark.start_time = start_time
chaptermark.title = title
chaptermark.production = production
chaptermark.save()
return HttpResponseRedirect(reverse('fc:episodeshowlinks'))
return render(request, self.template_name, {'form': form})
chaptermark.py form:
from django import forms
class EpisodeChapterMarksForm(forms.Form):
production_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
chaptermark_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
start_time = forms.TimeField(required=False)
title = forms.CharField(max_length=200)
chaptermark template:
{% extends "fc/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-success active" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%">
<span class="sr-only">50% Complete</span>
</div>
</div>
<div class="panel panel-default box-shadow--16dp col-sm-6 col-sm-offset-3">
<div class="panel-body">
<div class='row'>
<div class='col-sm-12'>
{% if title %}
<h1 class='{% if title_align_center %}text-align-center{% endif %}'>{{ title }}<!-- : {{ get.clientsetup.company_name }} --></h1>
{% endif %}
{% if subtitle %}
<h3 class='{% if subtitle_align_center %}text-align-center{% endif %}'>{{ subtitle }}</h4>
{% endif %}
<h5>Chapter Marks</h5>
<form method='POST' action=''>{% csrf_token %}
{{ form|crispy }}
<hr/>
<button type="submit" class="btn btn-primary box-shadow--6dp"><i class="fa fa-chevron-right pull-right"></i> Continue
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
----------------------UPDATE-------------------------
Was in views.py:
#login_required
def episodechaptermarks(request):
title = 'Podcast'
title_align_center = True
subtitle = 'Setup | Add Episode'
subtitle_align_center = True
form = ChapterMarksForm(request.POST or None)
context = {
"title": title,
"subtitle": subtitle,
"form": form
}
if form.is_valid():
instance = form.save(commit=False)
start_time = form.cleaned_data.get("start_time")
title = form.cleaned_data.get("title")
instance.start_time = start_time
instance.title = title
instance.user = request.user
instance.save()
return render(request, "pod_funnel/forms_chapter_marks.html", context)
else:
return render(request, "pod_funnel/forms_chapter_marks.html", context)
ModelForm:
from django import forms
from producer.models import ChapterMark
class ChapterMarksForm(forms.ModelForm):
class Meta:
model = ChapterMark
fields = ['start_time', 'title']
def clean_start_time(self):
start_time = self.cleaned_data.get('start_time')
return start_time
def clean_title(self):
title = self.cleaned_data.get('title')
return title
In essence, your production object has a series of timestamps that relate back via a FK. You need a set of views for CRUD at the production level. Let's assume your models are already created. There's a few things from my experience I want to point out that I think will point you in the right direction.
Unless absolutely necessary never use a Form class when creating a form object that mirrors a model; you are introducing a need for unnecessary complexity and opening the door for errors. Use a ModelForm, which can save objects to the DB straight from the view and help you manage cleaning, validation, and more. In addition, these can easily mesh with generic views of all sorts.
For this sort of relation (a model object with a varying number of model objects of a given type relating back that object) Django provides the powerful but difficult inlineformset_factory. This creates a series of inline forms as needed for a relation such as this.
So you have a model (production) and another related back to that (timestamp). You need to save these at the same time, possibly perform cleaning or validation, and really provide CRUD functionality for this relationship as a whole. For this, you could create a complex view from scratch or you could use django-extra-views and their generic CBVs for models with inlines. You can subclass CreateWithInlinesView, UpdateWithInlinesView. Why? Most Django devs would agree formsets are difficult to implement.
So, to give you a simplified version of how you can do this
from extra_views.advanced import CreateWithInlinesView, InlineFormSet, UpdateWithInlinesView
class TimeStampsInline(InlineFormSet):
model = models.TimeStamp
form = TimeStampForm # If you haven't created a custom ModelForm, can also specify "fields= ['field_1','field_2',...] and the CBV will create a ModelForm
extra = 0
class ProductionCreate(CreateWithInlinesView):
model=models.Production
inlines = [TimeStampsInline]
success_url = reverse('production-list') # the url to return to on successful create
exclude = ['created_by'] # render all fields except this in form
template_name = 'myapp/update.html'
class ProductionUpdate(UpdateWithInlinesView):
model=models.Production
inlines = [TimeStampsInline]
success_url = reverse('production-list')
exclude = ['created_by']
template_name = 'myapp/update.html'
Your template(s) will have to be built in specification with formsets; there's documentation and tutorials all over for that.
That's already a lot to digest, but you probably get the general idea. Don't build a horse from scratch ;)

Django CBV Update ValueError Cannot assign None

After creating the initial data (CreateView) I then proceed to update more data (UpdateView), yet I get the following answer while tring to update the new data. I am attaching the relvelant parts of the code:
ValueError at /preinscripcion/padres/63
Cannot assign None: "Alumni.cycle" does not allow null values.
Request Method: POST
Request URL: http://decroly.administracionescolarmexico.com/preinscripcion/padres/63
Django Version: 1.5
Exception Type: ValueError
Exception Value:
Cannot assign None: "Alumni.cycle" does not allow null values.
Exception Location: /home/fbenavides/webapps/django/lib/python2.7/django/db/models/fields/related.py in __set__, line 401
Python Executable: /usr/local/bin/python
Python Version: 2.7.5
Model.py
# -*- coding: utf-8 -*-
# Create your models here
from django.db import models
from django.forms import ModelForm
from django.conf import settings
from django.core.files.storage import FileSystemStorage
import os.path
import datetime
# Create your models here
class ScholarCycle(models.Model):
"""
ScholarCycle
Yearly School Cycle (e.g. 2012-2013)
"""
cycle = models.CharField(blank=False, max_length=9, verbose_name=u'Ciclo Escolar')
def __unicode__(self):
return u'%s' % self.cycle
class Meta:
db_table = u'Ciclo Escolar'
ordering = ['id', 'cycle']
verbose_name = u'Ciclo Escolar'
verbose_name_plural = u'Ciclos Escolares'
class Admin:
pass
fs_photos = FileSystemStorage(location='/media/user/photos')
fs_docs = FileSystemStorage(location='/media/user/docs')
class Alumni(models.Model):
"""
"""
cycle = models.ForeignKey(ScholarCycle, unique=False, null=False, blank=True, verbose_name=u'Ciclo Escolar')
url.py
urlpatterns = patterns('',
url(r'^lista/$', views.AlumniList.as_view(), name='alumni-lista'),
url(r'^lista/(?P<page>\d+)$', views.AlumniRedirectMostrar.as_view(), name='alumni-redirect-mostrar'),
url(r'^mostrar/(?P<pk>\d+)$', views.AlumniRetrieve.as_view(), name='alumni-mostrar'),
url(r'^mostrar/padres/(?P<pk>\d+)$', views.AlumniRetrievePadres.as_view(), name='alumni-mostrar-padres'),
url(r'^mostrar/autorizado/(?P<pk>\d+)$', views.AlumniRetrieveAutorizado.as_view(), name='alumni-mostrar-autorizado'),
url(r'^mostrar/finanzas/(?P<pk>\d+)$', views.AlumniRetrieveFinanzas.as_view(), name='alumni-mostrar-finanzas'),
url(r'^mostrar/documentos/(?P<pk>\d+)$', views.AlumniRetrieveDocumentos.as_view(), name='alumni-mostrar-documentos'),
url(r'^inscripcion/$', views.AlumniRegister.as_view(), name='alumni-inscripcion'),
url(r'^inscripcion/padres/(?P<pk>\d+)$', views.AlumniRegisterPadres.as_view(), name='alumni-inscripcion-padres'),
url(r'^inscripcion/autorizado/(?P<pk>\d+)$', views.AlumniRegisterAutorizado.as_view(), name='alumni-inscripcion-autorizado'),
url(r'^inscripcion/finanzas/(?P<pk>\d+)$', views.AlumniRegisterFinanzas.as_view(), name='alumni-inscripcion-finanzas'),
url(r'^inscripcion/documentos/(?P<pk>\d+)$', views.AlumniRegisterDocumentos.as_view(), name='alumni-inscripcion-documentos'),
url(r'^preinscripcion/$', views.AlumniPreinscripcion.as_view(), name='alumni-preinscripcion'),
url(r'^preinscripcion/padres/(?P<pk>\d+)$', views.AlumniPreinscripcionPadres.as_view(), name='alumni-preinscripcion-padres'),
url(r'^preinscripcion/autorizados/(?P<pk>\d+)$', views.AlumniPreinscripcionAutorizados.as_view(), name='alumni-preinscripcion-autorizados'),
url(r'^preinscripcion/finanzas/(?P<pk>\d+)$', views.AlumniPreinscripcionFinanzas.as_view(), name='alumni-preinscripcion-finanzas'),
views.py
class AlumniPreinscripcion(LoginRequiredMixin, CreateView):
model = models.Alumni
form_class = forms.AlumniFormPreinscripcion
context_object_name = 'alumno'
template_name = 'alumni_preinscripcion.html'
def get_initial(self):
initial = super(AlumniPreinscripcion, self).get_initial()
initial['cycle'] = models.ScholarCycle.objects.get(cycle__exact='2014-2015')
return initial
def get_success_url(self, **kwargs):
return reverse('alumni-preinscripcion-padres', kwargs={'pk':self.object.id})
class AlumniPreinscripcionPadres(LoginRequiredMixin, UpdateView):
model = models.Alumni
form_clas = forms.AlumniFormPreinscripcionPadres
context_object_name = 'alumno'
template_name = 'alumni_preinscripcion_padres.html'
def get_sucess_url(self, **kwargs):
return reverse('alumni-preinscripcion-autorizados', kwargs='pk':self.object.id})
alumni_preinscripcion_padres.html
{% extends "preinscripcion.html" %}
{% load i18n %}
{% load l10n %}
{% block content %}
<h2>Padres</h2>
<form action="" method="post" enctype="multipart/form-data" accept-charset="utf-8">
{% csrf_token %}
<fieldset>
<legend><strong> Papá </strong></legend>
<div class="row-fluid">
<div class="span2">
<p>Nombre:</p>
</div>
<div class="span10">
<p>{{ form.father_name }} {{ form.father_familynames }}</p>
</div>
</div>
<br />
<p><input type="submit" value="Persona(s) Autorizada(s) →" /></p>
</form>
{% endblock %}
The cycle field on your Alumni model is defined with null=False, blank=True. You probably want to change that. I can't tell you the correct values because that depends on your application logic, but those values are working at odds with your form views. From the documention:
null is purely database-related, whereas blank is validation-related.
If a field has blank=True, form validation will allow entry of an
empty value. If a field has blank=False, the field will be required.
Read that entire section. Once you understand the proper settings for your Model fields, the forms and class-based-views will most likely work just fine. (FYI, the reason your CreateView works is because you specifically set initial['cycle'] which satisfies the null criteria whereas you don't do that in UpdateView and the update form thinks it's okay to have blank/empty values.)
Update: There are two typos in your AlumniPreinscripcionPadres view, get_sucess_url() and form_clas, both missing a letter.