Storing user's avatar upon registration - django

I have an extended UserProfile for registering new users. My user_created function connects to signals sent upon registering basic User instance and creates new UserProfile with extended fields from my form. Here's the code :
from registration.signals import user_registered
from accounts.forms import ExtendedRegistrationForm
import accounts
from accounts.models import UserProfile
def user_created(sender, user, request, **kwargs):
form = ExtendedRegistrationForm(request.POST, request.FILES)
data = UserProfile(user=user)
data.is_active = False
data.first_name = form.data['first_name']
data.last_name = form.data['last_name']
data.pid = form.data['pid']
data.image = form.data['image']
data.street = form.data['street']
data.number = form.data['number']
data.code = form.data['code']
data.city = form.data['city']
data.save()
user_registered.connect(user_created)
Problem is that on this form I have an image field for avatar. As you can see from the code, I'm getting data from form's data list. But apparently imageField does not send it's data with POST request(as I'm getting MultiValueDictKeyError at /user/register/, Key 'image' not found in <QueryDict...) so I can't get it from data[] .
alt text http://img38.imageshack.us/img38/3839/61289917.png
If the usual variables are inside 'data', where should I look for files ? Or is the problem more complicated ? Strange thing is that my form doesn't have attribute cleaned_data... I was using dmitko's method here : http://dmitko.ru/?p=546&lang=en . My :
forms : http://paste.pocoo.org/show/230754/
models : http://paste.pocoo.org/show/230755/

You should be validating the form before using it, which will create the "cleaned_data" attribute you're used to. Just check form.is_valid() and the "cleaned_data" attribute will be available, and should contain the file.
The form's "data" attribute is going to be whatever you passed in as its first initalization argument (in this case, request.POST), and files are stored separately in the "files" attribute (whatever you pass in as the second argument, in this case, request.FILES). You don't want to be accessing the form's "data" or "files" attributes directly, as, if you do, you're just reading data straight from the request and not getting any benefit from using forms.

Are you sure the <form enctype="..."> attribute is set to multipart/form-data ? Otherwise the browser is not able to upload the file data.

Related

"form.populate_by returns" ERROR:'list' object has no attribute

I am creating a view function to edit the database using a wtform, I want to populate the form with information held on the database supplied by a differente form, My problem is the query that provides the details
I have read the manual https://wtforms.readthedocs.io/en/stable/crash_course.html
and the following question Python Flask-WTF - use same form template for add and edit operations
but my query does not seem to supply the correct format of data
datatbase model:
class Sensors(db.Model):
id = db.Column(db.Integer, primary_key=True)
sensorID = db.Column(db.String, unique=True)
name = db.Column(db.String(30), unique=True)
form model:
class AddSensorForm(FlaskForm):
sensorID = StringField('sensorID', validators=[DataRequired()])
sensorName = StringField('sensorName', validators=[DataRequired()])
submit = SubmitField('Register')
view function:
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.\
query(Sensors).filter_by(id=id).all()]
form = AddSensorForm(obj=edit)
form.populate_obj(edit)
if form.validate_on_submit():
sensors = Sensors(sensorID=form.sensorID.data, sensorName=form.sensorNa$
db.session.add(sensors)
db.session.commit()
shell code for query:
from homeHeating import db
from homeHeating import create_app
app = create_app()
app.app_context().push()
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.query(Sensors).filter_by(id=id).all()]
print(edit)
editsensor(1)
[('28-0000045680fde', 'Boiler input')]
I expect that the two form fields will be populated with the in formation concerning the sensor called by its 'id'
but I get this error
File "/home/pi/heating/homeHeating/sensors/sensors.py", line 60, in
editsensor
form.populate_obj(edit)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/form.py", line 96, in populate_obj
Open an interactive python shell in this
framefield.populate_obj(obj, name)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/fields/core.py", line 330, in populate_obj
setattr(obj, name, self.data)
AttributeError: 'list' object has no attribute 'sensorID'
The error indicates that it wants 2 parts for each field "framefield.populate_obj(obj, name) mine provides only one the column data but not the column name, "sensorID"
If i hash # out the line "edit = ..." then there are no error messages and the form is returned but the fields are empty. So I want the form to be returned with the information in the database, filled in so that i can modify the name or the sensorID and then update the database.
I hope that this is clear
Warm regards
paul.
ps I have followed the instruction so the ERROR statement is only the part after "field.populate_by".
You are trying to pass a 1-item list to your form.
Typically, when you are selecting a single record based on the primary key of your model, use Query.get() instead of Query.filter(...).all()[0].
Furthermore, you need to pass the request data to your form to validate it on submit, and also to pre-fill the fields when the form reports errors.
Form.validate_on_submit will be return True only if your request method is POST and your form passes validation; it is the step where your form tells you "the user provided syntactically correct information, now you may do more checks and I may populate an existing object with the data provided to me".
You also need to handle cases where the form is being displayed to the user for the first time.
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
obj = Sensors.query.get(id) or Sensors()
form = AddSensorForm(request.form, obj=obj)
if form.validate_on_submit():
form.populate_obj(obj)
db.session.add(obj)
db.session.commit()
# return response or redirect here
return redirect(...)
else:
# either the form has errors, or the user is displaying it for
# the first time (GET)
return render_template('sensors.html', form=form, obj=obj)

Passing MultiValueDict to Django ModelForm

I'm working with a ModelForm and passing data that was stored in the DB using a MultiValueDict that holds the original post data, in the form of u'first_name': [u'XX'], u'last_name': [u''] and such, but the form renders the value completely to the actual field, including [u''] wrapping the rest.
If I try to pass the same MultiValueDict to a regular Form, I don't get this problem.
I'm clueless on how to handle the dictionary or to process the data before assigning it as the initial value.
Edit:
Code that gets the data from post and restores it to the form
if request_post_data is not None:
self.form = self.FORM_CLASS(data=request_post_data)
self.draft.raw_data = dict(request_post_data) # preserves multiple values per key
self.draft.save()
else:
# conversion to MultiValueDict restores multiple values per key
self.form = self.FORM_CLASS(initial=MultiValueDict(self.draft.raw_data) if self.draft.raw_data else None)`
request_post_data comes from a serialized field in the db and an actual value looks like this:
<MultiValueDict: {u'last_name': [u''], u'suffix': [u''], u'prefix': [u''], u'first_name': [u'XX'], u'middle_name': [u'']}>
which ends up showing in my forms like this (when sent to ModelForm):
Actual form
Thanks for your help!
In order to instantiate a form with an instance and the provided POST query dict you can do the following in your view:
project = get_object_or_404(Project.objects, pk=project_id)
projectForm = ProjectForm(request.POST,
instance=project)
In order to provide some initial values you have to provide the initial values in a dictionary using the initial keyword argument.
projectForm = ProjectForm(initial={'name': 'my first project'})

django - how can I clean variable data passed by an url?

When I'm using a form I clean field data using Django forms but how do you clean variable data that's passed by an URL?
For example I have an URL like this: http://mywebsite.com/tags/my-tag/ where my-tag is the variable that I'm passing to a function on my views.py.
I tried to use a Django form to clean the data but I'm getting en error saying "'TagForm' object has no attribute 'cleaned_data'".
I know my-form variable is reaching the tags function in the views.py since I'm able to show its content on a template so the problem is probably with the way I'm using the form.
views.py
def tags(request, my-tag):
tagform = TagForm(request.GET)
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
forms.py
class TagForm(forms.Form):
tag = forms.CharField()
Any ideas?
The cleaned_data dictionary attribute appears after you call is_valid method on your form.
def tags(request, my-tag):
tagform = TagForm(request.GET)
if tagform.is_valid():
cleaned_dt = tagform.cleaned_data
form_tag = cleaned_dt['tag']
return render(request, "may_template.html", {"form":tagform})
You are creating a TagForm with a request object, but you're not giving the TagForm the value of my-tag anywhere that I can see.
The /my-tag/ section of the URL isn't a request parameter. It's part of the url, and presumably passed to the view function as my-tag (you might want to rename it my_tag to be more Pythonic).
Edit
You can simple create a dict object to initialize to Form object instead of request.GET. An example is here.
data = {'tag': my_tag,
'anotherIfNecessary': 'Hi there'}
tagform = TagForm(data)
Basically, the dictionary used to populate a form object must contain a mapping of form field names to the value you wish to set it at.
In this case, you have a form field name of "tag" and want to set it to my-tag (are you sure you don't get a syntax error with the dash in the variable name? I do...). I've corrected my example.

Help understanding a Django view

I am trying to follow the code listed on https://github.com/alex/django-ajax-validation/blob/master/ajax_validation/views.py
I have been able to understand a small chunk of it. I have added comments stating my understanding of what is happening.
I would really appreciate some assistance on questions I listed in comments next to the lines I couldn't quite follow.
def validate(request, *args, **kwargs):
# I thing it is some sort of initializations but I cannot really understand what's happening
form_class = kwargs.pop('form_class')
defaults = {
'data': request.POST
}
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
kwargs = extra_args_func(request, *args, **kwargs)
defaults.update(kwargs)
form = form_class(**defaults)
if form.is_valid(): #straightforward, if there is no error then the form is valid
data = {
'valid': True,
}
else:
# if we're dealing with a FormSet then walk over .forms to populate errors and formfields
if isinstance(form, BaseFormSet): #I cannot really understand what is BaseFromSet
errors = {}
formfields = {}
for f in form.forms: # I am guessing that this is for when there are multiple form submitted for validation
for field in f.fields.keys(): # I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?
formfields[f.add_prefix(field)] = f[field]
for field, error in f.errors.iteritems():
errors[f.add_prefix(field)] = error
if form.non_form_errors():
errors['__all__'] = form.non_form_errors() # what is the '__all__'?
else:
errors = form.errors
formfields = dict([(fieldname, form[fieldname]) for fieldname in form.fields.keys()])
# if fields have been specified then restrict the error list
if request.POST.getlist('fields'): # I am having a hard time understanding what this if statement does.
fields = request.POST.getlist('fields') + ['__all__']
errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])
final_errors = {} # here the author of this code totally lost me.
for key, val in errors.iteritems():
if '__all__' in key:
final_errors[key] = val
elif not isinstance(formfields[key].field, forms.FileField):
html_id = formfields[key].field.widget.attrs.get('id') or formfields[key].auto_id
html_id = formfields[key].field.widget.id_for_label(html_id)
final_errors[html_id] = val
data = {
'valid': False or not final_errors,
'errors': final_errors,
}
json_serializer = LazyEncoder() # Why does the result have to be returned in json?
return HttpResponse(json_serializer.encode(data), mimetype='application/json')
validate = require_POST(validate) # a decorator that requires a post to submit
LazyEncoder
class LazyEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return force_unicode(obj)
return obj
form_class = kwargs.pop('form_class')
This is simply pulling the keyword argument, form_class, that was passed in via the URL conf.
(r'^SOME/URL/$', 'ajax_validation.views.validate',
{'form_class': ContactForm}, # this keyword argument.
'contact_form_validate')
BaseFormSet is simply the formset class doing the work behind the scenes. When you don't know, search the source! grep -ri "baseformset" . It's an invaluable tool.
Take a look at at django.forms.formsets to see how formset_factory produces new "formset" classes based on the BaseFormSet, hence the factory part!
I am guessing that this is for when there are multiple form submitted for validation
Yes, that's exactly what a formset is for (dealing with multiple forms)
I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?
Yes, that would be looping through the field names.
add_prefix() is for prefixing form field names with a specific form. Because a formset repeats form elements multiple times, each field needs a unique prefix, such as 0-field1, 1-field1, etc.
formfields is just an empty dictionary defined a few lines above.
what is the 'all'?
__all__ is defined at the top of django.forms.forms
NON_FIELD_ERRORS = '__all__'
It's just what non field specific errors (such as constraints across 2 fields) are stored under in the errors dictionary as opposed to errors[fieldname].
I am having a hard time understanding what this if statement does.
The author has left a note:
# if fields have been specified then restrict the error list
if request.POST.getlist('fields'):
It's checking if you specified any specific fields to validate in your URLConf, this is not django but ajax_validation.
You can see that he's overwriting his errors dictionary based on only the fields specified, thus passing on the validation only for those fields.
errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])
here the author of this code totally lost me.
The author has mapped a custom errors and fields dictionary to specific field names with prefixes, (as opposed to the usual FormSet with each form having its own errors dictionary, unaware of the formset itself) which he presumably uses in the AJAX response to validate all fields.
Normally, you can iterate over a formset and go through the errors on a form by form basis, but not so if you need to validate all of them through ajax.
The line pulling html_id should be straight forward most of the time, but it's there because form widgets CAN add interesting things to the end of the ID's based on whether or not the widget is a radio select for example.
From source comments :
# RadioSelect is represented by multiple <input type="radio"> fields,
# each of which has a distinct ID. The IDs are made distinct by a "_X"
# suffix, where X is the zero-based index of the radio field. Thus,
# the label for a RadioSelect should reference the first one ('_0').
Why does the result have to be returned in json?
Because it's an ajax request and javascript easily eats json.
2- could you go through these lines of code...
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
Either return a keyword argument named 'callback' (which if passed in, is supposed to be a function that accepts request and return a dictionary), and if it wasn't, return a lambda function that only returns an empty dictionary.
I'm not sure what the specific use is for the extra context. You could use it to run arbitrary snippets of code without modifying or subclassing ajax_validation...
It might help you to run this code, and put a debugger breakpoint in somewhere so you can step through and examine the variables and methods. You can do this by simply putting this line where you want to break:
import pdb; pdb.set_trace()
and you will be dumped into the debugger in the console.

Best way to handle request variables in Django

I have a form 'in the wild' that takes many different variables - which may or may not be populated.
try:
app_version = request.REQUEST["appVersion"]
except:
app_version = ''
try:
app_name = request.REQUEST["appName"]
except:
app_name = ''
try:
app_code_name = request.REQUEST["appCodeName"]
except:
app_code_name = ''
Is there a tighter way to accomplish this?
app_version = request.REQUEST.get("appVersion", "")
get(key, default) is a method implemented on Python dicts. If the key exists in the dictionary, its value is returned; if the key does not exist, the specified default value is returned. In Django, request objects are dictionary-like objects, so get is also defined for them in the same manner.
If these variables are intended to populate a form, then you can safely pass the request.POST object directly into the form constructor.
if request.method == 'POST':
form = MyForm(request.POST)
The form will automatically pass the correct values to the correct form fields and use defaults for keys that don't exist and will still create blank fields for missing keys (see addendum).
If you are trying to process a form, it is still better to create a form object as above, and read out the values from that object.
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
# You may process these variables here
print form.appVersion
print form.appName
print form.appCodeName
Remember, validation code is best placed in the form class as well. That way, if form.is_valid() returns True, then you know you have a clean dataset to work with.
Note: Django docs recommend using request.POST or request.GET directly rather than the amalgamated variable request.REQUEST, as it is more explicit.
Addendum:
It is important to understand the difference between bound and unbound forms in this case. If you create an unbound form with form = MyForm(), then when the form is instantiated, it will fill in all fields with the initial property of each field (if it exists). For example, with this code:
from django import forms
class MyForm(forms.Form):
appVersion = forms.CharField(initial='1.0')
appName = forms.CharField()
appCodeName = forms.CharField()
the form will be initialized with appVersion having a value of '1.0'. However, if you bind a POST request to a form like this: form = MyForm(request.POST), then the initial properties are ignored. That means if the POST dict does not include an appVersion key, then that field will be left blank. As long as the field is not required, your form will still validate, and you can modify form.appVersion in the view after validation.
If you have many fields, a more compact version might be:
defaults = { 'field1' : 'val1', 'field2' : 'val2', ...}
defaults.update(request.POST)