Python - Setting an attribute to the value of a method - cleanest way? - python

I want a nice convenient attribute to do the following:
from django.contrib.auth.models import User
user = User.objects.get(id=2)
user.company
<Company: Big Company L.L.C>
I am currently solving this using lambda. In searching for an answer it looks like perhaps the "right" way to solve this would be to use types.MethodType but I can't seem to get my head around it. Yes, I have read Raymond excellent guide but I'm clearly missing something.. Here is my current solution for those who are interested..
# Defined Elsewhere
class User:
name = models.CharField(max_length=32)
class Company(models.Model):
users = models.ManyToManyField(User, related_name="companies", blank=True, null=True)
# Here is the meat of this..
class UserProfile(models.Model):
"""This defines Users"""
user = models.OneToOneField(User)
def get_company(self):
try:
companies = self.user.companies.all()[0]
except (AttributeError, IndexError):
return None
User.company = property(lambda u: UserProfile.objects.get_or_create(user=u)[0].get_company())
Right now this works.. But is there a better way - I'm not crazy about lambdas??

I'm not quite sure I understand correctly what your goal is, but from what I think I understand, it doesn't seem necessary to do any crazy stuff with descriptors here, let alone types.MethodType. A simple property is fine, and if you don't like the lambda, you can use an ordinary function decorated with #property:
class User:
name = models.CharField(max_length=32)
#property
def company(self):
return UserProfile.objects.get_or_create(user=self)[0].get_company())
Edit: If you can't touch the User class, you can create a derived class adding the desired property:
class MyUser(User):
#property
def company(self):
return UserProfile.objects.get_or_create(user=self)[0].get_company())

Building on #SvenMarnach's answer, you can still accomplish the same thing without using lambda. Though you still have to monkey-patch:
def _get_user_company(user):
return UserProfile.objects.get_or_create(user=user)[0].get_company()
User.company = property(_get_user_company)

Related

Reuse wtforms field in multiple forms

I have a bunch of forms that have similar fields. To simplify my code, I'd like to define the fields outside of the forms and then add the fields to the forms as needed like this:
name = wt.StringField("name")
age = wt.StringField("age")
class Form1(FlaskForm):
name=name
class Form2(FlaskForm):
age=age
class Form3(FlaskForm):
name=name
age=age
This pattern seems to work, but I've never seen anyone do this before so I want to make sure that there are not edge cases where this will break. If so, are there better ways of doing this?
Although the pattern in my question works, I'm nervous about using it since it doesn't appear to be a recommended way of doing things. This is a safer approach that meets my needs:
def name_field(): return wt.StringField("name")
def age_field(): return wt.StringField("age")
class Form1(FlaskForm):
name=name_field()
class Form2(FlaskForm):
age=age_field()
class Form3(FlaskForm):
name=name_field()
age=age_field()
It seems to me FormField is what you're looking for.
class nameForm(Form):
name = wt.StringField("name")
class ageForm(Form):
age = wt.StringField("age")
class Form1(FlaskForm):
name = FormField(nameForm)
class Form2(FlaskForm):
age = FormField(ageForm)
class Form3(FlaskForm):
name = FormField(nameForm)
age = FormField(ageForm)
Here's some relevant documentation -> https://wtforms.readthedocs.io/en/2.3.x/fields/
Search this doc for the Field Enclosures Section

django - Creating custom queryset for RelatedManager

I tried about 25 Stackoverflow links and nobody seems to have a working solution for this problem.
I created a custom queryset like this:
class ProjectQuerySet(models.QuerySet):
def get_active(self):
from apps.projectmanagement.models import Project
return self.filter(active=True)
class ProjectManager(models.Manager):
def get_queryset(self):
return ProjectQuerySet(self.model, using=self._db)
This works great if I start at the model like
Project.objects.get_active()
But if I want to use it in a relation, no luck so far:
employee.projects.get_active()
I always get this error:
AttributeError: 'ManyRelatedManager' object has no attribute 'get_active'
What I've tried so far:
I read that use_for_related_fields = True in the manager class is deprecated. Does not work anyway on django v2.1
Adding this in my model, as half the internet states:
class Project(models.Model):
...
objects = ProjectManager()
class Meta:
base_manager_name = 'objects'
Trying to avoid the RelatedManager and to work with a queryset:
employee.projects.all().get_active()
Any ideas what I've been doing wrong? And how would I solve this? Can't be too hard, right?
As the docs state, "Base managers aren’t used when querying on related models". The example they give is for going in the other direction, ie project.employee.
There is no way to do this using managers themselves. The best bet is to define a method on your model:
class Employee(models.Model):
...
def get_active_projects(self):
return self.projects.filter(active=True)
# or
return Project.objects.filter(employee=self).get_active()

Django related User Model - Permissions and Decorators

i am a beginner to Django i am trying to use permissions to permit access to specific view functions via a decorator, for specific user types only. Right now i am totaly confused by all kinds of stuff i have read about and seam not to figure out how i should do this.
I have two different kinds of users let it be UserTypeONE and UserTypeTWO.
UserTypeONE and UserTypeTWO should have access to specific views only.
Here is my code:
myuserTypes.py
class UserTypeONE(models.Model):
lieOtO_User = models.OneToOneField(settings.AUTH_USER_MODEL)
lie_SomeAttribute= models.CharField(max_length=300, help_text ='Name')
class Meta:
permissions = (('Can_View_MyShop', 'Can see Shop View'),)
class UserTypeTWO(models.Model):
lieOtO_User = models.OneToOneField(settings.AUTH_USER_MODEL)
lie_SomeOtherAttribute= models.CharField(max_length=300, help_text ='Name')
class Meta:
permissions = (('Can_View_Targets', 'Can see the Targets'),)
Here is what i am trying to do in my views.py
#login_required
#permission_required('UserTypeONE.Can_View_MyShop', raise_exception=True)
def MyShopView(request):
#do something
i also tried
#user_passes_test(lambda u: u.usertypeone.permission('Can_View_MyShop'))
As you guys can see i am an absolute beginner unfortunately all documentations and examples havent done me any good instead i am even more confused.
I would really appreciate help on this.
I would use user_passes_test() here since you specifically want to restrict specific views.
First, define a couple of functions that return True when you're dealing with a user who should be able to see your content. It looks like your UserTypeOne and UserTypeTwo models extend the base User model with a one-to-one relationship, so you can use hasattr to check if a given base user has one of those attributes:
def type_one_only(user):
if hasattr (user, 'usertypeone'):
return True
else:
return False
def type_two_only(user):
#same thing without if/else
return hasattr(user, 'usertypetwo')
Now when you have a view that you want to restrict to one user type, you can add a user_passes_test decorator before it:
#user_passes_test(type_one_only, login_url='/')
def my_view(request):
...
login_url is where a user will be sent if they do not pass the test you've indicated.

Pythonic way of using getters and setters

I would like a Pythonic way of accomplishing the following task. To call a method in Campaign Model that returns a URL with the campaign ID and the current login user ID i.e. /campaign_id/user_id
To achieve this I need the request.user scope, but I don't think its a good idea to have this directly in my models. I have been reading Python can use getters and setters, would this be useful? So my idea is to have something like this...
campaign = Campaign.object.get(id=1)
campaign.set_current_user(request.user)
referral_url = campaign.get_referral_url()
which would give me /campaign_id/user_id/
Is this a good way?
My Model so far:
class Campaign(models.Model):
name = models.CharField(max_length=120)
#property
def current_user(self):
return self._current_user
#current_user.setter
def current_user(self, user_object):
if user_object:
return user_object.id
else:
return None
def _build_referral_url(self):
"""
Builds the full referral URL for the user.
:return:
"""
return self.name + "/" + self.current_user
def get_referral_url(self):
"""
:return: Campaign referral URL.
"""
return self._build_referral_url()
Instead of:
def get_referral_url(self):
simply define:
def get_referral_url_for(self, user):
This looks the most straightforward, and doesn't give a false impression that user in question would be somehow permanently connected to the Campaign.

Django - Checking the type of Multi-table inheritence Querysets

I'm trying to hold a kind of table of contents structure in my database. Simplified example:
models.py
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class SectionClickable(Section):
link = models.CharField(max_length=80)
class SectionHeading(Section):
background_color = models.CharField(max_length=6)
views.py
sections = Section.objects.filter(title="Hello!")
for section in sections:
if(section.sectionheading):
logger.debug("It's a heading")
I need to do some processing operations if it's a SectionHeading instance, but (as in the Django manual), accessing section.sectionheading will throw a DoesNotExist error if the object is not of type SectionHeading.
I've been looking into alternatives to this kind of problem, and I'm skimming over Generic Foreign Keys in the contenttypes package. However, this seems like it would cause even more headaches at the Django Admin side of things. Could anyone advise on a better solution than the one above?
Edit: I avoided abstract inheritence because of the order field. I would have to join the two QuerySets together and sort them by order
well you could check the type:
if isinstance(section, SectionHeading)
but duck typing is generally preferred
edit:
actually, that probably won't work. the object will be a Section. but you can look for the attribute:
if hasattr(section, 'sectionheading')
or
try:
do_something_with(section.sectionheading)
except AttributeError:
pass # i guess it wasn't one of those
The solution I came up using involved an extra field pointing to the (rather useful) ContentType class:
class Section(models.Model):
name = models.CharField(max_length=50)
content_type = models.ForeignKey(ContentType,editable=False,null=True)
def __unicode__(self):
try:
return self.as_leaf_class().__unicode__()
except:
return self.name
def save(self, *args, **kwargs):
if(not self.content_type):
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Section, self).save(*args, **kwargs)
def as_leaf_class(self):
content_type = self.content_type
model = content_type.model_class()
if(model == Section):
return self
return model.objects.get(id=self.id)
If you're going through "base" object, I think this solution is pretty nice and comfortable to work with.
I've been using something similar to what second suggests in his edit:
class SomeBaseModel(models.Model):
reverse_name_cache = models.CharField(_('relation cache'), max_length=10,
null=True, editable=False)
def get_reverse_instance(self):
try:
return getattr(self, self.reverse_name_cache)
except AttributeError:
for name in ['sectionclickable', 'sectionheading']:
try:
i = getattr(self, name)
self.reverse_name_cache = name
return i
except ObjectDoesNotExist:
pass
Now, this isn't exactly pretty, but it returns the subclass instance from a central place so I don't need to wrap other statements with try. Perhaps the hardcoding of subclass reverse manager names could be avoided but this approach was enough for my needs.
OP here.
While second's answer is correct for the question, I wanted to add that I believe multi-table inheritence is an inefficient approach for this scenario. Accessing the attribute of the sub-class model would cause a query to occur - thus requiring a query for every row returned. Ouch. As far as I can tell, select_related doesn't work for multi-table inheritence yet.
I also ruled out ContentTypes because it wouldn't do it elegantly enough and seemed to require a lot of queries also.
I settled on using an abstract class:
class Section (models.Model):
title = models.CharField(max_length=80)
order = models.IntegerField()
class Meta:
abstract=True
ordering=['order']
Queried both tables:
section_clickables = SectionClickable.objects.filter(video=video)
section_headings= SectionHeading.objects.filter(video=video)
and joined the two querysets together
#Join querysets http://stackoverflow.com/questions/431628/how-to-combine-2-or-more-querysets-in-a-django-view
s = sorted(chain(section_headings, section_clickables), key=attrgetter('order'))
Lastly I made a template tag to check the instance:
from my.models import SectionHeading, SectionClickable
#register.filter()
def is_instance(obj, c):
try:
return isinstance(obj, eval(c))
except:
raise ObjectDoesNotExist('Class supplied to is_instance could not be found. Import it in the template tag file.')
so that in my template (HamlPy) I could do this:
- if s|is_instance:"SectionClickable"
%span {{s.title}}
- if s|is_instance:"SectionHeading"
%span{'style':'color: #{{s.color}};'}
{{s.title}}
The result is that I only used two queries, one to get the SectionClickable objects and one for the SectionHeading objects

Categories