Flask with SQLAlchemy persisting large form (newbie) - python

I'm writing a lot of boilerplate like code to try and persist my form data. In other languages I've not had to manually map the form attribute to the model prior to saving I'm wondering does python/flask has a similar approach? I'd ideally prefer to manually map all my form fields to simply persist a db record.
[edit]
To be clear this approach works fine for me the records are persisted properly however it seems kind of inefficient. In another piece of code I was able to use the wtf library {{ wtf.quick_form(form) }}
to create a form an html form without explicitly listing all its fields. Given I have the same names for my model attributes as I do for the form attributes I was wondering if I can do the same?
[edit]
Any feedback
Example below..
I have a largish model "product"
class Product(db.Model):
field1 = db.Column(db.String(200))
field2 = db.Column(db.String(200))
....
field30 = db.Column(db.String(200))
It's represented as a flask form e.g.
class ProductForm(FlaskForm):
field1 = StringField('Field 1', validators=[DataRequired()])
field2 = TextAreaField('Field 2 Label')
field30 = TextAreaField('Field 30 Label')
submit = SubmitField('Submit')
Here I try and map and persist the product record...
def save_product(self, product, form):
product.field1 = form.field.data
#lots more tedious form to model mapping
db.session.add(product)
# commit the data to the database
db.session.commit()
In my routes.py I have the following. I see it being called and the record does indeed persist.
def crud():
form = CrudForm()
if form.validate_on_submit():
product = Product()
product.save_product(product, form, new=True)
flash(_('Product Saved'))
return redirect(url_for('main.index'))
return render_template('crud.html', title=_('Product CRUD'),
form=form)

Given that the names of your form fields are the same as the names of your database columns it is possible to cut down on the boilerplate. A dict of all of the fields in your form is accessible as the form.data property and this can be passed directly through as keyword arguments when your create the new instance of your model rather than setting them one at a time afterwards.
product = Product(**form.data)

Related

How to handle concurrency with django queryset get method?

I'm using django (1.5 with mysql) select_for_update method for fetching data from one model and serve this data to user upon request, but when two user request at simultaneously it returns same data for both of the user, see the sample code below
models.py
class SaveAccessCode(models.Model):
code = models.CharField(max_length=10)
class AccessCode(models.Model):
code = models.CharField(max_length=10)
state = models.CharField(max_length=10, default='OPEN')
views.py
def view(request, code):
# for example code = 234567
acccess_code = AccessCode.objects.select_for_update().filter(
code=code, state='OPEN')
acccess_code.delete()
SaveAccessCode.objects.create(code=code)
return
Concurrent request will generate two records of SaveAccessCode with same code, Please guide me how to handle this scenario in better way
You need to set some flag on the model when doing select_for_update, something like:
qs.first().update(is_locked=True)`
and before that should do select like
qs = self.select_for_update().filter(state='OPEN', is_locked=False).order_by('id')
Then after the user, I presume, has done something with it and saved, set the flag is_locked=False and save.
Also make the fetch_available_code as a #staticmethod.

How to define a selection field in flask

I want to define a selection field in python, i.e. field that is limited to a set of values. How can I do that in flask framework. I could not find anything on selection fields in the following sources:
Declaring Models
SQLAlchemy in Flask
I am using sqlalchemy for ORM.
I assume you mean a field in a form that has a limited set of options; to do this you can use WTForms and its extensions which allow you to create forms from models.
Once you have done that, you can then limit the choices for a field based on a model condition.
As you haven't posted your model, here is the example give you give you an idea on how this would work:
def enabled_categories():
return Category.query.filter_by(enabled=True)
class BlogPostEdit(Form):
title = TextField()
blog = QuerySelectField(get_label='title')
category = QuerySelectField(query_factory=enabled_categories,
allow_blank=True)
def edit_blog_post(request, id):
post = Post.query.get(id)
form = ArticleEdit(obj=post)
# Since we didn't provide a query_factory for the 'blog' field, we need
# to set a dynamic one in the view.
form.blog.query = Blog.query.filter(Blog.author == request.user) \
.order_by(Blog.name)

Python WTForms: How to dynamically set options of a SelectField in FieldList of FormFields?

I am currently trying to use WTForms's FieldList and FormField enclosures to allow users to add a custom subset of locations with corresponding coverage amounts.
The form presents a user with some standard inputs and then initializes with a single set of fields (FormField) each with a location select input and a coverage amount input.
Javascript allows the user to add or remove additional location coverage field sets as needed to reflect the accurate document information.
The Problem: As a workaround, I have been setting the location options by passing a template variable in the form handler's GET request and manually creating my own form fields. This isn't updating the actual WTForms location field choices though, so when I submit the form an exception is raised for the location field ('Not a valid choice').
How can I dynamically add location choices to the location field of the LocationForm when I instantiate MyForm?
This is basically how my code looks:
Note: I have omitted the code that creates a locations template variable in the GET request since that is not the desired design. I want to be more in line with the intended WTForms methodology:
class LocationForm(Form):
location = SelectField('Location', [], choices=[])
coverage = FloatField('Coverage', [])
class MyForm(BaseForm):
# other fields omitted for brevity
location_coverage = FieldList(FormField(LocationForm), [], min_entries=1)
class AddDocument(BaseHandler):
def get(self):
params = {
"cid": cid
}
return self.render_template("form.html", **params)
def post(self):
cid = self.request.get('cid')
if not self.form.validate():
return self.get()
company_key = ndb.Key('Company', cid)
doc = Document(parent=company_key)
self.form.populate_obj(doc)
doc.put()
params = {
"cid":
}
return self.redirect_to('view_company', **params)
#webapp2.cached_property
def form(self):
f = MyForm(self)
# HERE is where I would normally do something like:
# company = ndb.Key('Company', int(self.request.get('cid')))
# locations = ndb.Location.query(ancestor=company).fetch()
# f.field_name.choices = [(loc.key, loc.name) for loc in locations]
# but this doesn't work with Select Fields enclosed in
# FormFields and FieldLists.
return f
Edit:
I created a solution but this isn't the answer I am looking for. In my case, I simply changed the LocationForm.location form field from a SelectField to a StringField. Doing this bypasses the validation of the select field choices and allows the form to submit. This is not ideal as it is not the intended design, but if anyone can steer me toward a more proper way to use WTForms in this particular scenario I would greatly appreciate it.
If your BaseForm class populates the form from the post data on instantiation, you should see the nested forms populated at the point where you'd normally add the choices into a SelectField directly on the form.
Therefore something like:
for entry in f.location_coverage.entries:
entry.location.choices = [(loc.key, loc.name) for loc in locations]
Should populate the choices into each subform select field.

Returning extended fields in JSON

I have two tabels(Ingredient_Step and Ingredient) in on relation as you can see below:
Models.Py
class Ingredient_Step(models.Model):
ingredient = models.ForeignKey(Ingredient)
Step = models.ForeignKey(Step)
def __unicode__(self):
return u'{}'.format(self.Step)
class Ingredient(models.Model):
IngredientName = models.CharField(max_length=200,unique=True)
Picture = models.ImageField(upload_to='Ingredient')
def __unicode__(self):
return u'{}'.format(self.IngredientName)
In a function, i need serialize a JSON object from a query that returns from "Ingredient_step", but I need send the field "IngredientName", who comes from "Ingredient" table.
I try using "ingredient__IngredientName" but it fails.
Views.Py:
def IngredientByStep(request):
if request.is_ajax() and request.GET and 'id_Step' in request.GET:
if request.GET["id_Step"] != '':
IngStp = Ingredient_Step.objects.filter(Step =request.GET["id_Step"])
return JSONResponse(serializers.serialize('json', IngStp, fields=('pk','ingredient__IngredientName')))
How i can call extends field from a relation?
Thanks
This "feature" of Django (and many ORM's like SQLAlchemy) are called Lazy Loading, meaning data is only loaded from related models if you specifically ask for them. In this case, build your IngStp as a list of results, and make sure to access the property for each result before serializing.
Here's an example of how to do that: Django: Include related models in JSON string?

Where's the primary key of the object I want to update in django using modelform?

I'm using modelform in django to insert and update objects in my database, but when I try to update I cannot see the primary key/id of the object being updated:
My model:
class Category(models.Model):
name = models.CharField(max_length=20, db_index = True)
and my form:
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ['name']
and in my template I got:
{% csrf_token %}
{{ category_form.as_p }}
In my view I do
cat = Category.objects.get(pk = cat_id)
data['category_form'] = CategoryForm(instance = cat)
and pass the data to my template, which renders the form ok, but the id of the object I about to update is nowhere in the html source. How can the code then now what object to update?
I feel stupid asking this since it should be pretty basic, but I've followed all the tutorials and looked thru the django docs, googled and search this site without luck.
Thanks in advance.
Where is cat_id coming from in your view? I guess you receive it in url, like so:
url( r'categories/(\d+)/edit/', your_view, {} ),
in urls.py somewhere. Now in your view you can read it from appropriate view function argument:
def your_view( request, cat_id ):
Now you can obtain object with proper id, which you do here:
cat = Category.objects.get(pk = cat_id)
...and instantiate ModelForm passing it cat object if you want to edit existing object, or don't pass it, if you want an empty form for object creation.
The explanation for this can be found in the django docs here: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
While trying to update already saved entity you must provide an instance parameter when you recreate the form. Otherwise django will try to insert a new entity.
foo_form = FooForm(request.POST, instance=foo)
The primary key is an attribute called "id" on your instance object "cat". The form itself, and in your example represented by "cat_id". The Model form should takes care to track the primary key - all you should need to do is pass the resulting "request.POST" data back into CategoryForm, valid the data with is_valid() and then save it.
i.e.
form_with_post = CategoryForm(request.POST)
if form_with_post.is_valid():
form_with_post.save()
else:
... return the form_with_post through the context to display the errors
The ID doesn't need to be in the HTML, because you've passed the instance into the form object when you instantiate it. Django simply updates that instance when you do form.save().

Categories