SQLAlchemy Query - Dynamic class selection - python

I can find many examples of how to build these queries where the 'filter_by' part is dynamic, but I cant seem to find anything where the class name is the dynamic bit. I'm sure an answer must be out there, just not sure what to search for.
My use case it this: I need to build a dynamic SQLAlchemy query. The twist is that its the class name changing rather than the filter variables. The query type will always be a '.get()' so I'm good there. I should also say that simply plugging a variable in where the class name should be doesnt work.
db_model = request.values.get("db_model_class")
item_id = request.values.get("item_id")
result = db.session.query(db_model).get(int(item_id))
How do I go about making this work?

Create a lookup of relevant classes:
models = {"Foo": Foo,
"Bar": Bar,
"...": ...}
and get the class:
db_model = models[request.values.get("db_model_class")]

db_model = request.values.get("db_model_class")
item_id = request.values.get("item_id")
Maybe the problem is here.
you need to set a debug point.
the answer by Ilja Everilä was great. so you need to check the way you get the parameters.
if you are using a pycharm, configure a debug setting.
if you are using Macbook,
click the run->edit configurations->
then you click the left margin of the code, and restart your app with the debug button on the top right corner. when you request the url again, you will see what your view function has get in the request object.

Related

Solution needed to a scenario

I am trying to make use of a column's value as a radio button's choice using below code
Forms.py
#retreiving data from database and assigning it to diction list
diction = polls_datum.objects.values_list('poll_choices', flat=True)
#initializing list and dictionary
OPTIONS1 = {}
OPTIONS = []
#creating the dictionary with 0 to no of options given in list
for i in range(len(diction)):
OPTIONS1[i] = diction[i]
#creating tuples from the dictionary above
#OPTIONS = zip(OPTIONS1.keys(), OPTIONS1.values())
for i in OPTIONS1:
k = (i,OPTIONS1[i])
OPTIONS.append(k)
class polls_form(forms.ModelForm):
#retreiving data from database and assigning it to diction list
options = forms.ChoiceField(choices=OPTIONS, widget = forms.RadioSelect())
class Meta:
model = polls_model
fields = ['options']
Using a form I am saving the data or choices in a field (poll_choices), when trying to display it on the index page, it is not reflecting until a server restart.
Can someone help on this please
of course "it is not reflecting until a server restart" - that's obvious when you remember that django server processes are long-running processes (it's not like PHP where each script is executed afresh on each request), and that top-level code (code that's at the module's top-level, not in a function) is only executed once per process when the module is first imported. As a general rule: don't do ANY db query at a module's top-level or at the top-level of a class statement - at best you'll get stale data, at worse it will crash your server process (if you're doing query before everything has been properly setup by django, or if you're doing query based on a schema update before the migration has been applied).
The possible solutions are either to wait until the form's initialisation to setup your field's choices, or to pass a callable as the formfield's choices options, cf https://docs.djangoproject.com/en/2.1/ref/forms/fields/#django.forms.ChoiceField.choices
Also, the way you're building your choices list is uselessly complicated - you could do it as a one-liner:
OPTIONS = list(enumerate(polls_datum.objects.values_list('poll_choices', flat=True))
but it's also very brittle - you're relying on the current db content and ordering for the choice value when you should use the polls_datum's pk instead (which is garanteed to be stable).
And finally: since you're working with what seems to be a related model, you may want to use a ModelChoiceField instead.
For future reference:
What version of Django are you using?
Have you read up on the documentation of ModelForms? https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/
I'm not sure what you're trying to do with diction to dictionary to tuple. I think you could skip a step there and your future self will thank you for that.
Try to follow some tutorials and understand why certain steps are being taken. I can see from your code that you're rather new to coding or Python and there's room for improvement. Not trying to talk you down, but I'm trying to push you into the direction of becoming a better developer ;-)
REAL ANSWER:
That being said, I think the solution is to write the loading of the data somewhere in your form model, rather than 'loose' in forms.py. See bruno's answer for more information on this.
If you want to reload the data on each request that loads the form, you should create a function that gets called every time the form is loaded (for example in the form's __init__ function).

Odoo - Changing user group id just right after signup (ecommerce)

I'm using Odoo 10. After a new user sign up (through localhost:8069/web/signup) i want him to be automatically allocated inside a group i created on my very own custom module (the user will need authentication from an admin later on so he can be converted to a regular portal user; after signup he will receive restricted access).
I have tried many things. My latest effort looks like this:
class RestrictAccessOnSignup(auth_signup_controller.AuthSignupHome):
def do_signup(self, *args):
super(RestrictAccessOnSignup, self).do_signup(*args)
request.env['res.groups'].sudo().write({'groups_id': 'group_unuser'})
Note that I have import odoo.addons.auth_signup.controllers.main as auth_signup_controller so that I can override the auth_signup controller.
I have located that method as the responsible for doing the signup. So I call it in my new method and then try to change the newly created user's group_id.
What i miss is a fundamental understanding of how to overwrite a field's value from another model inside a controller method context. I'm using the 'request' object although i'm not sure of it. I have seen people using 'self.pool['res.users'] (e.g.) for such purposes but i don't understand how to apply it inside my problem's context.
I believe, also, that there is a way to change the default group for a user after it is created (i would like to know), but i also want to understand how to solve the general problem (accessing and overwriting a field's value from another module).
Another weird thing is that the field groups_id does exist in 'res.users' model, but it does not appear as a column in my pgAdmin interface when i click to see the 'res.users' table... Any idea why?
Thanks a lot!
i don't know if after calling :
super(RestrictAccessOnSignup,self).do_signup(*args)
you will have access to user record in request object but if so just add
the group to user like this, if not you have to find where the user record or id is saved after calling do_signup because you need to update that record to ad this group.
# create env variable i hate typing even i'm typing here ^^
env = request.env
env.user.sudo().write({'groups_id': [
# in odoo relation field accept a list of commands
# command 4 means add the id in the second position must be an integer
# ref return an object so we return the id
( 4, env.ref('your_module_name.group_unuser').id),
]
})
and if changes are not committed in database you may need to commit them
request.env.cr.commit()
Note: self.env.ref you must pass the full xmlID.
This is what worked for me:
def do_signup(self, *args):
super(RestrictAccessOnSignup, self).do_signup(*args)
group_id = request.env['ir.model.data'].get_object('academy2', 'group_unuser')
group_id.sudo().write({'users': [(4, request.env.uid)]})
In the get_object i pass as arguments the 'module' and the 'xmlID' of the group i want to fetch.
It is still not clear to me why 'ir.model.data' is the environment used, but this works as a charm. Please note that here we are adding a user to the group, and not a group to the user, and to me that actually makes more sense.
Any further elucidation or parallel solutions are welcome, the methods aren't as clear to me as they should be.
thanks.

flask_admin change inline_models behaviour

I want to change some existing Python code that uses flask_admin. One of the views uses inline_models with the (ClassName, Options) declaration pattern. The inlined class has, amongst others, a text field.
I want to change the flask_admin default behaviour in the following ways:
I want to make the text field read-only. I.e. still display it, but prevent the user from changing existing content.
I do not want to allow users to delete instances of the inlined class, i.e. I want to get rid of the "Delete?" checkbox next to every entry.
I want to override the default "Add Item" button behaviour with some custom JavaScript.
I did some Googling around but anything that looked potentially promising also looked very non-trivial. I'm hoping for some reasonably straight forward way to achieve this.
Your help would be much appreciated.
Yeesh. It looks like we're out in poorly-documented territory, here. It's hard to know if I'm improving on what you've already found, but I'll hope you're looking for something easier than writing a custom administrative view template.
Following the calls, it looks like the options dictionary eventually gets passed to the constructor of InlineBaseFormAdmin where the various form_* keys are extracted and applied (not sure all are respected, but I see at least form_base_class, form_columns, form_excluded_columns, form_args, form_extra_fields, form_rules, form_label, form_column_labels, form_widget_args). I think you can accomplish what you need via form_widget_args, but you can probably also get there via form_rules or by overriding InlineBaseFormAdmin's get_form or postprocess_form methods:
class SomeModelView(MyBaseModelView):
...
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
The delete option can be controlled by providing your own inline form model to override display_row_controls:
from flask_admin.contrib.sqla.form import InlineModelConverter
from flask_admin.contrib.sqla.fields import InlineModelFormList
class CrouchingTigerHiddenModelFormList(InlineModelFormList):
def display_row_controls(self, field): return False
class MyInlineModelConverter(InlineModelConverter):
inline_field_list_type = CustomInlineModelFormList
#adding to above example
class SomeModelView(MyBaseModelView):
...
inline_model_form_converter = MyInlineModelConverter
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
NOTE: The widget args, such as readonly, are getting passed on to wtforms as render_kw, but at a blush the WTForms docs aren't clear that these get expressed as attributes in the resulting HTML input element (so any HTML input element attributes are valid here).
It looks like form.js controls this behavior, so you should be able to monkey-patch its addInlineField method to execute your own code before or after the model addition. You could override the create and/or edit templates for this--but if you're using flask-admin 1.5.0+, this might be as simple as adding extra_js = ["your-custom.js"] to the view class (caution: it looks like this script gets included on every page for this view).

Python : Singleton class object to persist list between imports ? (Like django admin register)

I want to have dict / list to which I can add values, just like models can be added to the admin register in django !
My attempt : (package -> __init__.py)
# Singleton object
# __init__.py (Package: pack)
class remember:
a = []
def add(data):
a.append[data]
def get():
return a
obj = remember()
# models1.py
import pack
pack.obj.add("data")
# models2.py
import pack
pack.obj.add("data2")
print pack.obj.get()
# We should get: ["data", "data2"]
# We get : ["data2"]
How to achieve the desired functionality ?
Some say that methods can do this if you don't need sub-classing, how to do this with methods ?
Update:
To be more clear :
Just like django admin register any one can import and register itself with admin, so that register is persisted between imports.
If it's a singleton you're after, have a look at this old blog post. It contains a link to a well documented implementation (here).
Don't. If you think you need a global you don't and you should reevaluate how you are approaching the problem because 99% of the time you're doing it wrong.
If you have a really good reason to do it perhaps thread_locals() will really solve the problem you're trying to solve. This allows you to set up thread level global data. Note: This is only slightly better than a true global and should in general be avoided, and it can cause you a lot of headaches.
If you're looking for a cross request "global" then you most likely want to look into storing values in memcached.

Recursive delete in google app engine

I'm using google app engine with django 1.0.2 (and the django-helper) and wonder how people go about doing recursive delete.
Suppose you have a model that's something like this:
class Top(BaseModel):
pass
class Bottom(BaseModel):
daddy = db.ReferenceProperty(Top)
Now, when I delete an object of type 'Top', I want all the associated 'Bottom' objects to be deleted as well.
As things are now, when I delete a 'Top' object, the 'Bottom' objects stay and then I get data that doesn't belong anywhere. When accessing the datastore in a view, I end up with:
Caught an exception while rendering: ReferenceProperty failed to be resolved.
I could of course find all objects and delete them, but since my real model is at least 5 levels deep, I'm hoping there's a way to make sure this can be done automatically.
I've found this article about how it works with Java and that seems to be pretty much what I want as well.
Anyone know how I could get that behavior in django as well?
You need to implement this manually, by looking up affected records and deleting them at the same time as you delete the parent record. You can simplify this, if you wish, by overriding the .delete() method on your parent class to automatically delete all related records.
For performance reasons, you almost certainly want to use key-only queries (allowing you to get the keys of entities to be deleted without having to fetch and decode the actual entities), and batch deletes. For example:
db.delete(Bottom.all(keys_only=True).filter("daddy =", top).fetch(1000))
Actually that behavior is GAE-specific. Django's ORM simulates "ON DELETE CASCADE" on .delete().
I know that this is not an answer to your question, but maybe it can help you from looking in the wrong places.
Reconsider the data structure. If the relationship will never change on the record lifetime, you could use "ancestors" feature of GAE:
class Top(db.Model): pass
class Middle(db.Model): pass
class Bottom(db.Model): pass
top = Top()
middles = [Middle(parent=top) for i in range(0,10)]
bottoms = [Bottom(parent=middle) for i in range(0,10) for middle in middles]
Then querying for ancestor=top will find all the records from all levels. So it will be easy to delete them.
descendants = list(db.Query().ancestor(top))
# should return [top] + middles + bottoms
If your hierarchy is only a small number of levels deep, then you might be able to do something with a field that looks like a file path:
daddy.ancestry = "greatgranddaddy/granddaddy/daddy/"
me.ancestry = daddy.ancestry + me.uniquename + "/"
sort of thing. You do need unique names, at least unique among siblings.
The path in object IDs sort of does this already, but IIRC that's bound up with entity groups, which you're advised not to use to express relationships in the data domain.
Then you can construct a query to return all of granddaddy's descendants using the initial substring trick, like this:
query = Person.all()
query.filter("ancestry >", gdaddy.ancestry + "\U0001")
query.filter("ancestry <", gdaddy.ancestry + "\UFFFF")
Obviously this is no use if you can't fit the ancestry into a 500 byte StringProperty.

Categories