Django custom method won't show up - python

I have two custom methods for a model manager in Django. One of them works. I recently added another and Django (and python) act like it doesn't exist. Here's the relevant part of the model:
class FigureServerManager(models.Manager):
#This method takes as input a user and grabs a figure that is not marked complete for which that user has not already submitted a result
def serve_to_user(self,user):
not_complete=super(FigureServerManager, self).get_query_set().filter(complete=0)
for Figure in not_complete:
checkifresult=User.objects.get(pk=user).result_set.all().filter(figure=Figure.id)
if not checkifresult:
return Figure
#This is a copy of the above method that I want to change to do something else, but I can't even get it to show up yet
def serve_training_task(self, user):
with_correct_ans=super(FigureServerManager, self).get_query_set().filter(complete=0)
for Figure in with_correct_ans:
checkifresult=User.objects.get(pk=user).result_set.all().filter(figure=Figure.id)
if not checkifresult:
return Figure
class Figure(models.Model):
doi=models.CharField(max_length=20)
url=models.CharField(max_length=200)
image=models.ImageField(upload_to='classify')
complete=models.BooleanField()
#include the default manager
objects=models.Manager()
#add the extra one for serving figures
serve_objects=FigureServerManager()
I get an error on the website (running the Django development server) like this:
'FigureServerManager' object has no attribute 'serve_training_task'
and if I run dir(FigureServerManager) in python the serve_training_task method does not appear but the serve_to_user method does appear. Why doesn't serve_training_task work?

Python Language Reference, ยง2.1.8: "Indentation"

Related

Unittest sensitive_post_parameters decorator in django view

I have a view to create new users in my django project.
I am applying the #sensitive_post_parameters decorator to that view to make sure the password isn't logged if there is an unhandled exception or something like that (as indicated in the comments in the source code https://docs.djangoproject.com/en/2.0/_modules/django/views/decorators/debug/).
When I proceed to test the view, I would like to make sure that this protection of the sensitive information is still in place (that I didn't delete the decorator to the function by mistake or something).
I am aware, since the decorator is applied to my function, I can't test it directly from the view tests.
But, for example, with the #login_required decorator, I can test its effects with assertRedirects (as explained here How to test if a view is decorated with "login_required" (Django)).
I have been searching for a way to do that, but I can't find one that works.
I thought of something like this:
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)
but that gives me an
AttributeError: 'WSGIRequest' object has no attribute 'sensitive_post_parameters'
Any help would be appreciated.
Even it is telling me I shouldn't be attempting to test this, though I would really like to, as it is seems like an important behaviour that I should make sure remains in my code as it is later modified.
You have created a request using RequestFactory, but you have not actually used it. To test the effect of your view you need to import the view and call it.
from myapp.views import create_user
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
response = create_user(request)
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)

Can't call a decorator within the imported sub-class of a cherrpy application (site tree)

I am using cherrypy as a web server, and I want to check a user's logged-in status before returning the page. This works on methods in the main Application class (in site.py) but gives an error when I call the same decorated function on method in a class that is one layer deeper in the webpage tree (in a separate file).
validate_user() is the function used as a decorator. It either passes a user to the page or sends them to a 401 restricted page, as a cherrypy.Tool, like this:
from user import validate_user
cherrypy.tools.validate_user = cherrypy.Tool('before_handler', validate_user)
I attach different sections of the site to the main site.py file's Application class by assigning instances of the sub-classes as variables accordingly:
from user import UserAuthentication
class Root:
user = UserAuthentication() # maps user/login, user/register, user/logout, etc
admin = Admin()
api = Api()
#cherrypy.expose
#cherrypy.tools.validate_user()
def how_to(self, **kw):
from other_stuff import how_to_page
return how_to_page(kw)
This, however, does not work when I try to use the validate_user() inside the Admin or Api or Analysis sections. These are in separate files.
import cherrypy
class Analyze:
#cherrypy.expose
#cherrypy.tools.validate_user() #### THIS LINE GIVES ERROR ####
def explore(self, *args, **kw): # #addkw(fetch=['uid'])
import explore
kw['uid'] = cherrypy.session.get('uid',-1)
return explore.explorer(args, kw)
The error is that cherrypy.tools doesn't have a validate_user function or method. But other things I assign in site.py do appear in cherrypy here. What's the reason why I can't use this tool in a separate file that is part of my overall site map?
If this is relevant, the validate_user() function simply looks at the cherrypy.request.cookie, finds the 'session_token' value, and compares it to our database and passes it along if the ID matches.
Sorry I don't know if the Analyze() and Api() and User() pages are subclasses, or nested classes, or extended methods, or what. So I can't give this a precise title. Do I need to pass in the parent class to them somehow?
The issue here is that Python processes everything except the function/method bodies during import. So in site.py, when you import user (or from user import <anything>), that causes all of the user module to be processed before the Python interpreter has gotten to the definition of the validate_user tool, including the decorator, which is attempting to access that tool by value (rather than by a reference).
CherryPy has another mechanism for decorating functions with config that will enable tools on those handlers. Instead of #cherrypy.tools.validate_user, use:
#cherrypy.config(**{"tools.validate_user.on": True})
This decorator works because instead of needing to access validate_user from cherrypy.tools to install itself on the handler, it instead configures CherryPy to install that tool on the handler later, when the handler is invoked.
If that tool is needed for all methods on that class, you can use that config decorator on the class itself.
You could alternatively, enable that tool for given endpoints in the server config, as mentioned in the other question.

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.

Customizing / extending / monkey patching Django Auth Backend

I am using django-auth-ldap to connect to an LDAP server for authentication. django-auth-ldap provides the setting AUTH_LDAP_REQUIRE_GROUP, which can be used to allow access only for users placed in a specific group. This works fine, but the option only allows to check one group; I want to check if a users is placed in either one or another group.
In the module django_auth_ldap/backend.py I could modify the method _check_required_groups of the class LDAPUser(object) to implement this behaviour. Modifying it directly works fine, but since changing the source would endup in a maintenance hell, I am searching for a solution to change this method without touching the source. Two ideas I had:
1) Monkey Patching
Change the _check_required_groups method of an instance of the LDAPUser class. The problem is that I have no idea where it is beeing instantiated. I am just using LDAPSearch and GroupOfNamesType imported from django_auth_ldap.config in the settings file, and passing the string django_auth_ldap.backend.LDAPBackend into the AUTHENTICATION_BACKENDS tuple.
2) Extending the module
Create an own module, extending the original django_auth_ldap and using this instead of the original. I tried to create a new directory, adding an __init__.py with the line:
from django_auth_ldap import *
But using this module does not work, since it can't import custom_auth.config.
Any other suggestions or hints how to make one of those attempts to work?
To be modular, DRY and true to the django philosophy in general, you need to create a class named LDAPBackendEx that would inherit from LDAPBackend and use this class to your AUTHENTICATION_BACKENDS instead of django_auth_ldap.backend.LDAPBackend. Also, you'dd create an LDAPUserEx that would inhert from _LDAPUser and override the _check_required_groups method.
So, the LDAPUserEx would be something like:
class LDAPUserEx(_LDAPUser):
def _check_required_group(self):
pass # Put your implementation here !
Now, concerning the implementation of LDAPBackendEx: Unfortuanately there is no way of defining a custom _LDAPUser class so you'd have to search every method that uses the _LDAPUser class and override it with LDAPUserEx. The correct methdod of implementing django-auth-ldap (and if we actually needed to be modular) would be to add an user_class attribute to LDAPBackend, initialize it to _LDAPUser and use that instead of _LDAPUser.
Checking the code here, I found out that the methods of LDAPBackend that refer _LDAPUser are authenticate, get_user and get_group_permissions. So, the implementation of LDAPBackendEx would be something like this:
class LDAPBackendEx(LDAPBackend):
def authenticate(self, username, password):
ldap_user = LDAPUserEx(self, username=username)
user = ldap_user.authenticate(password)
return user
def get_user(self, user_id):
pass # please put definition of get_user here changing _LDAPUser to LDAPUserEx
def get_group_permissions(self, user):
pass # please put definition of get_group_permissions here changing _LDAPUser to LDAPUserEx

How to inherit from class in same file when generating Django dynamic form

I'm fairly new to both Python and Django, and I'm in the process of building a small website. I've been experimenting with a dynamic form containing a ModelChoiceField constructed from a QuerySet that is determined by user input. The approach described here works well for me, but I run into trouble if I try to dynamically create a type that inherits from a class that I define within the same file. The reason I tried to do this was that I wanted to override the default form field order by setting the form's fields.keyOrder property.
This is how I tried to do it:
from django import forms
class AbstractForm(forms.Form):
def __init__(self, *args, **kwargs):
super(AbstractForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['choices', 'second', 'third']
class ConcreteFormFactory(object):
#staticmethod
def generate(queryset):
properties = {
'second': forms.CharField(),
'third': forms.CharField(),
'choices': forms.ModelChoiceField(queryset=queryset)
}
return type('ConcreteForm', (AbstractForm,), properties)
However, when I import ConcreteFormFactory in my view and attempt to call its generate() method, I get a NameError telling me that AbstractForm is not defined. I tried importing AbstractForm into the calling context, but to no avail. Since I wasn't able to get this approach to work, I accomplished my goal another way, but I would still like to understand the scoping issue here. How do I make my dynamic class aware of other classes defined in the same file where I create it? This is on Django 1.2 and Python 2.7.1 (I know that's a very old version of Django).
I apologize--this code DOES work as intended. I refactored my real code to use this pattern, and this time it worked. I must have had something else wrong.

Categories