I have the following files:
# in models.py
class User(object):
def __init__(self, name):
self.name = name
# in util.py
def get_user():
return User(name="tom")
# in views.py
from util import get_user
get_user().name
I want to detect all Attribute usages for the name attribute on the User model. So get_user().name would be flagged as one such usage, since get_user returns a role User object.
Is there an easy way to do this using Astroid only preferably?
Related
I am using Python 2.7, and have following code strucure
model
__init__.py
order.py
cart.py
That is, I define a package named model, and in this package, I define a module order, and I define a class in order.py
class MyOrder(object):
def __init__(self, name):
self.name = name
def getname(self):
return self.name
In the cart.py, the code is:
import model
x = model.order.MyOrder("Book")
print x.getname()
When I run it, it complains that AttributeError: 'module' object has no attribute 'order',
But the following is correct:
import model.order
x = model.order.MyOrder("Book")
print x.getname()
It looks that I can't import package (like import model) ?
If you want to have model automatically import order so it's available, you should do that in __init__.py. Simply put the following inside model/__init__.py:
from . import order
After that, you should be able to access model.order with just import model.
Following up on this question Flask-Admin Role Based Access - Modify access based on role I don't understand how to implement role-based views, especially regarding the form and column_lists.
Say I want MyModelView to show different columns if the user is a regular user or a superuser.
Overriding is_accessible in MyModelView has no effect at all
from flask_security import Security, SQLAlchemyUserDatastore, current_user
class MyModelView(SafeModelView):
# ...
def is_accessible(self):
if current_user.has_role('superuser'):
self.column_list = superuser_colum_list
self.form_columns = superuser_form_columns
else:
self.column_list = user_colum_list
self.form_columns = user_form_columns
return super(MyModelView, self).is_accessible()
# Has same effect as
def is_accessible(self):
return super(MyModelView, self).is_accessible()
and defining conditional class attributes does not work either as current_user is not defined (NoneType error as per AttributeError on current_user.is_authenticated()). Doing the same in the ModelView's __init__ being equivalent, current_user is still not defined
class MyModelView(SafeModelView):
#[stuff]
if current_user.has_role('superuser'):
column_list = superuser_colum_list
form_columns = superuser_form_columns
else:
column_list = user_colum_list
form_columns = user_form_columns
#[other stuff]
FYI, SafeModelView can be any class inheriting from dgBaseView in the previously mentioned question.
I usually define view class attributes such as column_list as properties. It allows you to add some dynamic logic to them:
from flask import has_app_context
from flask_security import current_user
class MyModelView(SafeModelView):
#property
def column_list(self):
if has_app_context() and current_user.has_role('superuser'):
return superuser_column_list
return user_column_list
#property
def _list_columns(self):
return self.get_list_columns()
#_list_columns.setter
def _list_columns(self, value):
pass
The problem with using this approach (and why your reassigning of column_list values in is_accessible function took no effect) is that many view attributes are cached on application launch and stored in private attributes. column_list for example is cached in _list_columns attribute so you need to redefine it as well. You can look how this caching works in flask_admin.model.base.BaseModelView._refresh_cache method.
Flask has_app_context method is needed here because first column_list read is happened on application launch when your current_user variable has no meaningful value yet.
The same can be done with form_columns attribute. The properties will look like this:
#property
def form_columns(self):
if has_app_context() and current_user.has_role('superuser'):
return superuser_form_columns
return user_form_columns
#property
def _create_form_class(self):
return self.get_create_form()
#_create_form_class.setter
def _create_form_class(self, value)
pass
#property
def _edit_form_class(self):
return self.get_edit_form()
#_edit_form_class.setter
def _edit_form_class(self, value):
pass
I was under the understanding that using the syntax:
from foo import y
will import the class y from foo.py.
If this is true how come when I use the following:
models.py
from django.db import models
from .utils import codeGenerator, createShortcode
class KirrUrlManager(models.Manager):
def all(self, *args, **kwargs):
qs_main = super(KirrUrlManager, self).all(*args, **kwargs)
qs = qs_main.filter(active=True)
return qs
def refreshShortCodes(self):
qs = KirrUrl.objects.filter(id__gte=1) #id_gte is primary key
newCodes = 0
for q in qs:
q.shortcode = createShortcode(q)
q.save()
newCodes +=1
return "new codes made: {i} ".format(i=newCodes)
class KirrUrl(models.Model):
url = models.CharField(max_length=220, unique=True)
shortcode = models.CharField(max_length=50, unique=True, blank = True)
timestamp = models.DateTimeField(auto_now=True,)
timestamp = models.DateTimeField(auto_now_add=True,)
active = models.BooleanField(default=True,)
objects = KirrUrlManager()
def save(self, *args, **kwargs):
if self.shortcode is None or self.shortcode =="":
self.shortcode = createShortcode(self)
super(KirrUrl, self).save(*args, **kwargs)
def __str__(self):
return str(self.url)
foo.py
from django.core.management.base import BaseCommand, CommandError
from shortener.models import KirrUrl
class Command(BaseCommand):
help = "refreshes all shortcodes"
def handle(self, *args, **options):
return KirrUrl.objects.refreshShortCodes()
I am unsure why I am able to call the method "refreshShortCodes()" in foo.py. I am only using the import statement "from shortener.models import KirrUrl". Shouldnt this import statement only let me import the KirrUrl class? refreshShortCodes() is not part of the KirrUrl class, however it is the models.py file that is being imported.
Shouldnt this import statement only let me import the KirrUrl class?
Yes, and that's exactly what the import statement is doing. objects.refreshShortCodes() is part of the KirrUrl class. So you have access to the class name, as well as its attributes.
When Python imports a class, it imports the entire class object. That means all of variables and methods defined in the classes namespace can be reached. So Since you created an instance of KirrUrlManager() inside of the KirrUrl class, you can access the refreshShortCodes() method by first getting the KirrUrlManager() instance:
KirrUrl.objects
And then getting the refreshShortCodes() method from the KirrUrlManager() instance:
KirrUrl.objects.refreshShortCodes()
You only need to import the class to have access to its attributes; you can't import an object encapsulated in a class.
Therefore, after importing the class KirrUrl, the model manager objects is accessible via the class, and the method refreshShortCodes is equally accessible via the model manager instance which is composed in the class KirrUrl.
This is one of the ways objects that are not reachable by the import mechanism are accessed; dot referencing.
I'm writing tests for a model in a Django app, but cannot get them to run. I've tried all I can think of to solve and searched but cannot find the solution.
The error I receive when I run the test is AttributeError: 'UserModelTest' object has no attribute 'firstUser', which would suggest I haven't defined firstUser correctly.
Here are the relevant bits of code.
tests.py
from django.test import TestCase
from .models import ContactForm
from datetime import datetime, timedelta
class UserModelTest(TestCase):
#classmethod
def setUpClass(cls):
cls.firstUser = ContactForm(
email="first#user.com",
name="first",
timestamp=datetime.today() + timedelta(days=2)
)
cls.firstUser.save()
def test_contactform_str_returns_email(self):
self.assertEqual("first#user.com", str(self.firstUser))
models.py
from django.db import models
import datetime
class ContactForm(models.Model):
name = models.CharField(max_length=150)
email = models.EmailField(max_length=250)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
class Meta:
ordering = ['-timestamp']
I'm using Python version 3.5 and Django version 1.10.
setUpClass has been deprecated for Django (I believe since 1.8). In order to set up the class now, I needed to use setUpTestData. The remaining code still works. setUpTestData is then run only once before all test methods in the class (not once before each test).
i.e. the code should be:
class UserModelTest(TestCase):
#classmethod
def setUpTestData(cls):
cls.firstUser = ContactForm(
email="first#user.com",
name="first",
timestamp=datetime.today() + timedelta(days=2)
)
cls.firstUser.save()
def test_contactform_str_returns_email(self):
self.assertEqual("first#user.com", str(self.firstUser))
More information on setUpTestData can be found here.
I'm having issues getting a class method to run in Flask.
In models/User.py:
from mongoengine import *
class User(Document):
first_name = StringField()
last_name = StringField()
...
def __init__(self, arg1, arg2, ...):
self.first_name = arg1
self.last_name = arg2
...
#classmethod
def create(self, arg1, arg2, ...):
#do some things like salting and hashing passwords...
user = self(arg1, arg2, ...)
user.save()
return user
In the main application python file:
from models import User
...
def func():
...
#Throws "AttributeError: type object 'User' has no attribute 'create'"
user = User.create(arg1, arg2, ...)
Shouldn't I be able to call create on the User class without instantiating a User object? I'm using Python 2.7.2, and I also tried the non-decorator syntax of using create = classmethod(create), but that didn't work. Thanks in advance!
EDIT: I found one issue: that the models folder did not contain an __init__.py file, so it wasn't a module, so from models import User was not actually importing the file I wanted it to. It did not give me an error from before because I used to have a models.py module in the same directory as the application python script, but after deleting it I never deleted the corresponding .pyc file. Now, I'm getting the error AttributeError: 'module' object has no attribute 'create' instead of what I had before, but I'm certain it is importing the correct file now.
EDIT2: Solved. I then changed the import to from models.User import User and It's hitting the method now.
The issue was twofold:
The User.py file was in the models/ folder, meaning that my import was actually looking for the User class in the models.py file, which no longer existed but still was being imported without error because the models.pyc file was still around
The import was incorrect for importing within a directory. it should have been from models.User import User, so long as the models/ folder is a module, so all I needed to do then was touch models/__init__.py.
>>> class foo(object):
... def __init__(self):
... pass
... #classmethod
... def classmethod(cls):
... return 0
...
>>> a = foo()
>>> a.classmethod()
0
>>>