I'm attempting to create a function to allow updates to fields based on the front-end input.
The handler would receive a profile_updates dictionary with two keys. Each will contain a list of key/value pairs.
list_of_updates['custom_permissions'] = [{"is_staff":"True"},{"other_permission":"False"}]
def update_profile(message):
list_of_updates = message['profile_updates']
user_update_id = message['user_id']
for update in list_of_updates['custom_permissions']:
for key, value in update.iteritems():
User.objects.filter(id=user_update_id).update(key=value)
I would like to make 'key' a variable fed from the .iteritems().
Appreciate anyone's input on how, or if, this is possible.
You don't need to loop through the dict. You can pass it as a kwarg expansion.
def update_profile(message):
list_of_updates = message['profile_updates']
user_update_id = message['user_id']
for update in list_of_updates['custom_permissions']:
User.objects.filter(id=user_update_id).update(**update)
Related
I have made a really long form with the help of colander alchemy and deform.
This form has 100 or so fields and currently the only way I know to add the data back to the database once form is submitted is to explicitly re-define each variable and then add that to the database but there must be a better way.
#my schema
class All(colander.MappingSchema):
setup_schema(None,atr)
atrschema =atr.__colanderalchemy__
setup_schema(None,chemicals)
chemicalsschema =chemicals.__colanderalchemy__
setup_schema(None,data_aquisition)
data_aquisitionschema =data_aquisition.__colanderalchemy__
setup_schema(None,depositor)
depositorschema =depositor.__colanderalchemy__
setup_schema(None,dried_film)
dried_filmschema =dried_film.__colanderalchemy__
form = All()
form = deform.Form(form,buttons=('submit',))
# this is how I get it to work by redefining each field but there must be a better way
if 'submit' in request.POST:
prism_material = request.params['prism_material']
angle_of_incidence_degrees =
request.params['angle_of_incidence_degrees']
number_of_reflections = request.params['number_of_reflections']
prism_size_mm = request.params['prism_size_mm']
spectrometer_ID = 6
page = atr (spectrometer_ID=spectrometer_ID,prism_size_mm=prism_size_mm,number_of_reflections=number_of_reflections,angle_of_incidence_degrees=angle_of_incidence_degrees,prism_material=prism_material)
request.dbsession.add(page)
Would like to somehow just be able to remap all of that 'multi dictionary' that is returned back to the database?
So, you have a dict (request.params) and want to pass the key-value pars from that dict to a function? Python has a way to do that using **kwargs syntax:
if 'submit' in request.POST:
page = Page(spectrometer_ID=6,**request.params)
request.dbsession.add(page)
(this works also because SQLAlchemy provides a default constructor which assigns the passed values to the mapped columns, no need to define it manually)
Of course, this is a naive approach which will only work for the simplest use-cases - for example, it may allow passing parameters not defined in your schema which may create a security problem; the field names in your schema must match the field names in your SQLAlchemy model; it may not work with lists (i.e. multiple values with the same name which you can access via request.params.get_all(name)).
I am trying to see if I can pass field name as a variable in get_or_create (since I have a function where the key in the kwargs can vary)
Like so:
def convert_value(cell_value, field_to_lookup):
rem_obj, created = Rem.objects.get_or_create(field_to_lookup=cell_value)
print ('created? ',created)
return rem_obj
The above wont work since it would look for 'field_to_lookup' as the key.
This post suggests using getattr but not sure if that'll be applicable in this case since I will again need to assign the output to a variable
This post helped. Now passing the field-value pair as dict which allows passing variables for field names. Here's the code:
def convert_value(cell_value, field_to_lookup):
rem_obj, created = Rem.objects.get_or_create(**{field_to_lookup:cell_value})
print ('created? ',created)
return rem_obj
Alternatively, I could directly just pass the dict to the function.
I have been using the #validates decorator in sqlalchemy.orm from flask to validate fields, and all has gone well as long as all of the fields are independent of one another such as:
#validates('field_one')
def validates_field_one(self, key, value):
#field one validation
#validates('field_two')
def validates_field_two(self, key, value):
#field two validation
However, now I need to do some validation that will require access to field_one and field_two simultaneously. It looks like validates accepts multiple arguments to the validates decorator, however, it will simply run the validation function once for each argument, as such:
#validates('field_one', 'field_two')
def validates_fields(self, keys, values):
#field validation
Results in a work flow of validate field_one and then validate field_two. However, I would like to validate both at the same time(a trivial example of which would be assert that the value of field_one is not the value of field_two, an example of which would be disallowing self-loops in a graph where field_one and field_two refer to nodes and it is performing validation on an edge). How would be the best way to go about doing that?
Order the fields in the order they were defined on the model. Then check if the last field is the one being validated. Otherwise just return the value unchecked. If the validator is validating one of the earlier fields, some of them will not be set yet.
#validates('field_one', 'field_two')
def validates_fields(self, key, value):
if key == 'field_two':
assert self.field_one != value
return value
See this example.
Adding another answer here, as the accepted one didn't quite meet my use case for using another field to validate and modify relationship/collection fields, which are not really compatible with #validates. In this case you can use an event listener for the before_flush event to achieve what you're looking for:
#event.listens_for(Session, 'before_flush')
def validate_and_modify_relationships(session, flush_context, instances):
"""
Complex validation that cannot be performed with #valdiates
"""
# new records only (for updates only, use session.dirty)
for instance in session.new:
if isinstance(instance, MyData):
if instance.some_other_value:
instance.some_relation = []
More details here: Flask-SQLAlchemy validation: prevent adding to relationship based on other field
I have defined optional variables in my django model. In my view, I might have those values or they might be None. I want to create that object without worrying about sending a None argument to the django model.
For example, Book object has a title, but publisher is optional.
right now in my view I'm doing something like
if publisher is None:
Book.objects.create(title=title)
else:
Book.objects.create(title=title, publisher=publisher)
Now this isn't manageable if there are multiple optional fields. What's the solution?
How about using ** operator:
attrs = {'title': title}
if publisher is not None:
attrs['publisher'] = publisher
Book.objects.create(**attrs)
UPDATE alternative - using Model.save:
book = Book(title='title')
if publisher is not None:
book.publisher = publisher
book.save()
Take a look at this:
Call a function with argument list in python
Basically create an array like args and then pass it in as *args to the method required.
You can also do something similar with **kwargs. Take a look at this:
Passing a list of kwargs?
Branching off the other answers... try this
def make_book(**kwargs):
query = {key: value for key, value in kwargs.items() if value}
Book.objects.create(**query)
I suggest declaring this as a method on your models manager so that you can quickly make instances where ever you need them like
Book.objects.make_book(title="The Title",publisher=None)
I'm trying to set a variable outside of the scope of the current loop.
My scenario is this: I have 2 lists. One containing a list of comment objects, and each comment has a reference to a user id. My second list contains all the user objects, based on the users id.
What I am trying to do is iterate through each comment, and then modify the comment object within the list to contain the users name, so that when I pass the list of comments back, it has the name embedded.
So far, this how I am trying to achieve this:
# iterate through the comments and add the display name to the comment obj
for comment in comments:
# Create the user to use later
user = None
# Iterate the comment_users and get the user who matches the current comment.
for comment_user in comment_users:
if comment_user['_id'] is comment['created_by']:
user = comment_user # this is creating a new user in the for comment_user loop
break
print(user)
# get the display name for the user
display_name = user['display_name']
# Add the user display name to the comment
comment.user_display_name = display_name
Now, from what I am starting to understand from Python's scope, is that the user = comment_user line in the second for loop is creating a new user variable within the scope of the second for loop, which is ignoring the user variable defined in the first for loop.
I'm using Python 3, so I thought that the nonlocal keyword would be the way to go, but I'm not sure if that is just for functions or not, as I couldn't get it to work.
So, I was wondering if anyone could provide a way to achieve this? Is there a more 'pythonic' way to achieve this?
I think the problem is your use of is. Try this code:
for comment in comments:
for comment_user in comment_users:
if comment_user['_id'] == comment['created_by']:
comment.user_display_name = comment_user['display_name']
break
This problem occurs when you are (incorrectly) using is to compare string objects. The equality operator (==) checks if the contents of the two strings are the same, whereas is operator actually checks if they are the same object. If the strings are interned, they may give the same result, but generally speaking you should never use is for string comparison.
I think a more pythonic way would be to make comment_user a dictionary that has the _id as key, so that you don't have to loop over the list but can just do
for comment in comments:
comment.user_display_name = comment_user[comment['created_by']]['display_name']