Property %s is required - python

I am write one easy program using GAE and python 2.7, but I met some problem while stored data into db. My code is below:
class MemberInfo(db.Model):
firstName = db.StringProperty(required=True)
class RegisterPageButtonDown(webapp2.RequestHandler):
def post(self):
memberInfo = MemberInfo()
memberInfo.firstName = self.request.get('firstName')
memberInfo.put()
The error raise in "memberInfo = MemberInfo()", it said "Property firstName is required". I am sure I put data in html form and the method is post, too.
I've been stuck in this problem for whole night, thanks for your reply.

You've set the firstName property to required, so when you instantiate an object you must provide that property with a value, e.g.:
memberInfo = MemberInfo(firstName = self.request.get('firstName'))
Alternatively, you can make firstName not required in your model.

The error is coming from the first line of the function, before you even get the value from the request. This is because you need to pass in that value when you instantiate the object.
firstName = self.request.get('firstName')
memberInfo = MemberInfo(firstName=firstName)
(Also note that normal naming conventions for Python mean that variables and properties are lower_case_with_underscore, not camelCase.)

Related

Update a field of a Django Object

I'm trying to update an object field in django. Usually I would do something like this:
# MODEL ---
class MyObj(models.model):
name: models.CharField(max_length=10)
surname: models.CharField(max_length=10)
# VIEW ---
# [...]
myObj = MyObj.objects.get(pk=1)
myObj.name = 'John'
myObj.save()
The problem is that the field to modify (in the example above: "name") is not known and passed as an argument to the post request. So I would have something like this:
# VIEW ---
# [...]
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
myObj[field] = 'John'
myObj.save()
now this triggers the error:
myObj[field] = 'John'
TypeError: 'MyObj' object does not support item assignment
What is the correct way to update an "unknown" field of a django object?
UPDATE
Thank you for your answers so far! OK so the way to go is apparently using setattr (as per the answers). Now the problem is that it does not allow me to save the modified object without checking if it is valid.
So I tried using the Serializer to check the object validity but is not working:
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
setattr(myObj, field, 'John')
serial = MyObjSerializer(myObj)
serial.is_valid(raise_exception=True)
serial.save()
error:
AssertionError: Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
You're looking for setattr to set an attribute by name.
field = self.request.query_params['field_to_modify']
# TODO: add validation for `field`
myObj = MyObj.objects.get(pk=1)
setattr(myObj, field, 'John')
myObj.save(update_fields=[field]) # no need to update anything but the single field
Of course, this won't let you automatically attach any arbitrary data to models and expect it to be saved; if you need something like that, maybe use a JSON field.
EDIT:
For using a Django REST Framework serializer (which, cough, wasn't part of the original question or tags, but could be inferred from query_params), one could do
field = self.request.query_params['field_to_modify']
myObj = MyObj.objects.get(pk=1)
serial = MyObjSerializer(instance=myObj, data={field: 'John'})
serial.is_valid(raise_exception=True)
serial.save()
Yeah it looks like Django model instances do not support that operation. It looks like you can use setattr though from looking at this previous question / answer. Update model instance with dynamic field names

Masking property in Datastore

I have a field in my collection that I would like to mask for responses. Here is a code example of what I need to achieve:
class Entity(ndb.Model):
name = ndb.StringField()
#property
def name(self):
return self.name [:2] + "***"
Expected result: name in database: John, name returned with API: Jo***
When I'm trying with code above getting
TypeError: Cannot set non-property name when trying to create an Entity
Is there any smarter way to do it than masking name on every response?
Is it possible to modify values in _pre_get_hook() just for a response without changing a field in the database?
I think what you need is _post_get_hook, something along the lines of
def _post_get_hook(self,key,future):
self.name = self.name[:2]+"***"
The fact that the name is modified is worrisome if a put is done on the same entity. I am not sure how your response is being constructed. If there is a way to filter some of the fields, then you can have a separate property that is derived based on the name in the _post_get_hook which is safer.

print referred variable from object in django/python

In the below python code ,can the object know that the template tag is referring a variable and get that in a python variable
newemp is the object that i am passing from the views and the template is trying to access a variable as {{newemp.get_names.emp_add}} ,now in the python code can the object print this variable i.e, emp_add
class Emp(models.Model):
name = models.CharField(max_length=255, unique=True)
address1 = models.CharField(max_length=255)
def get_names(self):
logging.debug(var)
var=self.some referred object
names = {}
No. The access is done once the appropriate object has been returned from get_names() so there is no direct way to know within the method what is being accessed.
If you are asking whether you can write to a variable within a template, and then access that value back within your Python code, I do not believe so. That kind of goes against the idea of templates, IMHO.

python in Google App Engine - having the class-instead-of-instance problem, but using db.Model so can't create init method

i am writing an app to compare products, using Python and GAE. The products will belong to a set of similar products, and the app calculates the best value in each set.
When i create a new product, it can be added to an existing set or a new set can be created.
When testing the app, the first set gets created just fine. I populate an instance of the set with the name of the product. I use a form on one web page to POST the data into the "suppbook" page. I'm still not clear on how a web page can be a class but that's a different question.
There's more code around all of this but I'm trying to make my question as clear as possible.
class Supp(db.Model):
name = db.StringProperty(multiline=False)
# a bunch of other attributes using Google's DB Model
class SuppSet(db.Model):
name = db.StringProperty(default='')
supp_list = set([])
# a bunch of other attributes using Google's DB Model
# i tried to add this after reading a few questions on SO but GAE doesn't like it
def __init__(self,):
self.name = 'NoName'
self.best_value = 'NoBestValue'
self.supp_list = set([])
Class Suppbook(webapp.RequestHandler):
def post(self):
supp = Supp()
suppSet = SuppSet()
...
supp.name = self.request.get('name')
supp.in_set = self.request.get('newset')
suppSet.name = supp.in_set
suppSet.supp_list.add(supp.name)
self.response.out.write('%s now contains %s<p>' % (suppSet.name,suppSet.supp_list))
This works well the first time around, and if I only use one SuppSet, I can add many supps to it. If I create another SuppSet, though, both suppSets will have the same contents for their supp_list. I have been looking through the questions on here and I think (know) I'm doing something wrong regarding class vs. instance attribute access. I tried to create an __init__ method for SuppSet but GAE complained: AttributeError: 'SuppSet' object has no attribute '_entity'
Also, I am using the GAE datastore to put() and get() the Supps and SuppSets, so I'm not clear why I'm not acting on the unique instances that I should be pulling from the DB.
I am not sure if I am providing enough information but I wanted to get started on this issue. Please let me know if more info is needed to help debug this.
I'm also open to the idea that i'm going about this completely wrong. I'm considering re-writing the whole thing, but I'm so close to being "finished" with basic functionality that I'd like to try to solve this issue.
Thanks
In your init you will need to call the super's init, db.Model has some important stuff to do in its init, you will have to match the signature.
However you likely shouldn't be setting up things like defaults in there. Try and just use the datastore Properties ability to set a default.
You've got some (I assume) typos in your code. Python is sensitive to case and white-space. The attribute names you use also don't match your defs, such as in_set. When possible, post actual working examples demonstrating your problem.
class Supp(db.Model):
name = db.StringProperty(multiline=False)
in_set = db.StringProperty(multiline=False)
# your other stuff ...
class SuppSet(db.Model):
name = db.StringProperty(default='')
supp_list = db.StringListProperty()
# your other stuff ...
# In Python, you need to explicitly call the parent's __init__ with your args.
# Note that this is NOT needed here.
def __init__(self, **kwargs):
db.Model.__init__(self, **kwargs)
class Suppbook(webapp.RequestHandler):
def post(self):
# This will create a NEW Supp and SuppSet every request,
# it won't fetch anything from the datastore.
# These are also NOT needed (included for explanation)
supp = Supp()
suppSet = SuppSet()
# It sounds like you want something like:
product_name = self.request.get('name')
product_set = self.request.get('newset')
# check for missing name / set:
if not product_name or not product_set:
# handle the error
self.error(500)
return
# Build the keys and batch fetch.
supp_key = db.Key.from_path('Supp', product_name)
suppset_key = db.Key.from_path('SuppSet', product_set)
supp, suppset = db.get([supp_key, suppset_key])
if not supp:
supp = Supp(key_name=product_name,
name=product_name)
if not suppset:
suppset = SuppSet(key_name=product_set,
name=product_set)
# Update the entities
supp.in_set = product_set
if product_name not in suppset.supp_list:
suppset.supp_list.append(product_name)
# Batch put...
db.put([supp, suppset])
self.response.out.write('%s now contains %s<p>' % (suppset.name, str(suppset.supp_list)))

converting django ForeignKey to a usable directory name

I'm working on a django app where the user will be able to upload documents of various kinds. The relevant part of my models.py is this:
class Materials(models.Model):
id = models.AutoField(primary_key=True)
id_presentations = models.ForeignKey(Presentations, db_column='id_Presentations', related_name = "materials_id_presentations") # Field name made lowercase.
materialpathname = 'documents/'
materialpathname += str(id_presentations)
document = models.FileField(db_column='Document', upload_to = materialpathname) # Field name made lowercase.
docname = models.CharField(max_length=40, db_column='DocName') # Field name made lowercase.
class Meta:
db_table = u'Materials'
My intention is for it to save the documents associated with a given presentation, in a subdirectory with the id number for that presentation (so if "Very Important Presentation" is on the database with id 3, it should store the associated materials at the location settings.MEDIA_ROOT/documents/3/whateverdocname.txt ).
However, while the above code "works", it creates a subdirectory that, instead of being named "3", is named <django.db.models.fields.related.ForeignKey object at 0x8e358ec>, or that kind of thing. I've tried using "id_presentations.name", "id_presentations.value", etc. but these attributes don't seem to exist. I can't seem to find a place where it gives a way to get at the integer value of the ForeignKey field, so that I can convert it to a string and use it as a subdirectory name.
Any help is greatly appreciated.
As of Django 1.0, the upload_to argument to FileFields can be a callable. If I'm understanding your intentions correctly, something like this should do the trick:
def material_path(instance, filename):
return 'documents/%d' % instance.id_presentations.id
class Materials(models.Model):
id_presentations = models.ForeignKey(Presentations)
document = models.FileField(upload_to=material_path)
docname = models.CharField(max_length=40)
That model has been simplified a little bit, but hopefully it illustrates the solution. If upload_to is a callable, then every time a file is uploaded Django will call the function, passing it two arguments: the instance to which the file was uploaded is attached and its original filename. You can generate the file path you want by pulling the ID of the presentation off of the instance in question.
More info:
http://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
Provided that the "name" property is defined on your Presentation model, if you're working with a specific instance of the model then what you want should work. Like this:
from models import Materials
obj = Materials.objects.get([some criteria here])
name = obj.id_presentation.name
If you wanted to abstract that to a method on your model, you could do this:
class Materials(models.Model):
def id_presentation_name(self):
return self.id_presentation.name
If you want the database id of the object, you can access either object.id or object.pk.
Note: you can always find out what attributes and methods are available on an object in python by calling dir(object).

Categories