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.
Related
May be I'm doing wrong way, so tell me how to do it better.
I'm implementing one class for all instances of some object, say users. (looks like it is pattern 'Table Module' in M. Fowler's "Patterns of Enterprise Application Architecture"). I attached the simplified example of my implementation.
user argument may be of different kinds:
just int ID
dictionary with information about user. it has key id among other
tuple or list with ID as element with index 0
I want to determine right type of input variable user and get ID from it. At the same time I'd like to check if the input is correct at all.
I'm confused about calling __get_id at the beginning of all methods handling user information.
I sure python can propose better and lighter solution of this problem. Or maybe the whole approach is wrong and I should implement another one.
Thanks in advance.
class BadInputError (Exception):
pass
class Users:
def __init__(self):
pass
def __is_wrong_id(self, id):
# code to check if `id` is wrong
return False
def __get_id(self, user):
if isinstance(user, int):
ID = user
elif isinstance(user, tuple) or isinstance(user, list):
ID = user[0]
elif isinstance(user, dict) and user.has_key('id'):
ID = user['id']
else:
raise BadInputError
if self.__is_wrong_id(ID):
raise BadInputError
return ID
def check_user(self, user):
uID = self.__get_id(user)
# ...
def delete_user(self, user):
uID = self.__get_id(user)
# ...
def assign_new_role_to(self, user):
uID = self.__get_id(user)
# ...
Here __get_id(self, user) make an attempt that would seem as overloading, however you're right - it's generally considered bad practice to check if a variable is an instance of a given class. In your definition you would only ever extract the first element of the tuple or list and hope that it corresponds to the ID.
Instead, you should strive to create wrappers of User that support the __get_id() method.
It's hard to tell with this stripped down code, but the problem that I see here is none of the methods actually use self (other than to call ___get_id -- which doesn't use self).
Normally a class stores data and associated methods -- If you just have methods, then it should be a module.
Here, it makes sense for a "User" to know it's ID, so it might be worth creating a "User" class which uses the __get_id function to store an attribute (self.id). Then the Users class (which represents a collection of Users) could just check user.id. e.g.:
class User(object):
def __init__(self, initialize_data):
self.__set_id(intialize_data)
def __set_id(self, data):
if isinstance(user, int):
self.id = user
elif isinstance(user, (tuple, list)):
self.id = user[0]
elif isinstance(user, dict) and user.has_key('id'):
self.id = user['id']
else:
raise BadInputError
class Users(object):
def __init__(self):
self.users = []
def delete_user(self, user):
self.users = [u for u in self.users if u.id != user.id]
Note that this example implementation is nowhere near optimal -- It was meant to show how I would structure the code. A bunch of improvements could probably be made to make it more efficient (i.e., maybe a User could be hashible based on the id in which case Users.users could be a set).
I have a django model that I want to attach an extra piece of information to, depending on the environment the instance is in (which user is logged in). For this reason, I don't want to do it at the database level.
Is this okay to do? Or are there problems that I don't foresee?
in models.py
class FooOrBar(models.Model):
"""Type is 'foo' or 'bar'
"""
def __init__(self, type):
self.type = type
in views.py
class FooCheck(FooOrBar):
"""Never saved to the database
"""
def __init__(self, foo_or_bar):
self.__dict__ = foo_or_bar.__dict__.copy()
def check_type(self, external_type):
if external_type == 'foo':
self.is_foo = True
else:
self.is_foo = False
foos_or_bars = FooOrBar.objects.all()
foochecks = map(FooCheck, foos_or_bars)
for foocheck in foochecks:
foocheck.check_type('foo')
extra credit question: Is there a more efficient way of calling a method on multiple objects i.e. replacing the last forloop with something clever?
Okay, this does not work. Trying to delete a FooOrBar objects throws a complaint about
OperationalError at /
no such table: test_FooCheck
To get around this I'm just not going to inherit from FooOrBar, but if anyone has a suggestion on a better way to do it I'd be interested in hearing it
I had a similar issue, I did something like:
class Foo(models.Model):
# specific info goes here
class Bar(models.Model):
# specific info goes here
class FooBar(models.Model):
CLASS_TYPES = {
"foo":Foo,
"bar":Bar
}
type = models.CharField(choices=CLASS_TYPES)
id = models.IntegerField()
#field to identify FooBar
then you can get the object back using
object = FooBar.CLASS_TYPES[instance.type].objects.get(id=instance.id)
where instance is the FooBar instance
I'm using tastypie and I want to create a Resource for a "singleton" non-model object.
For the purposes of this question, let's assume what I want the URL to represent is some system settings that exist in an ini file.
What this means is that...:
The fields I return for this URL will be custom created for this Resource - there is no model that contains this information.
I want a single URL that will return the data, e.g. a GET request on /api/v1/settings.
The returned data should return in a format that is similar to a details URL - i.e., it should not have meta and objects parts. It should just contain the fields from the settings.
It should not be possible to GET a list of such object nor is it possible to perform POST, DELETE or PUT (this part I know how to do, but I'm adding this here for completeness).
Optional: it should play well with tastypie-swagger for API exploration purposes.
I got this to work, but I think my method is kind of ass-backwards, so I want to know what is the common wisdom here. What I tried so far is to override dehydrate and do all the work there. This requires me to override obj_get but leave it empty (which is kind of ugly) and also to remove the need for id in the details url by overriding override_urls.
Is there a better way of doing this?
You should be able to achieve this with the following. Note I haven't actually tested this, so some tweaking may be required. A more rich example can be found in the Tastypie Docs
class SettingsResource(Resource):
value = fields.CharField(attribute='value', help_text='setting value')
class Meta:
resource_name = 'setting'
fields = ['value']
allowed_methods = ['get']
def detail_uri_kwargs(self, bundle_or_obj):
kwargs = {}
return kwargs
def get_object_list(self, request):
return [self.obj_get()]
def obj_get_list(self, request=None, **kwargs):
return [self.obj_get()]
def obj_get(self, request=None, key=None, **kwargs):
setting = SettingObject()
setting.value = 'whatever value'
return setting
The SettingObject must support the getattr and setattr methods. You can use this as a template:
class SettingObject(object):
def __init__(self, initial=None):
self.__dict__['_data'] = {}
if initial:
self.update(initial)
def __getattr__(self, name):
return self._data.get(name, None)
def __setattr__(self, name, value):
self.__dict__['_data'][name] = value
def update(self, other):
for k in other:
self.__setattr__(k, other[k])
def to_dict(self):
return self._data
This sounds like something completely outside of TastyPie's wheelhouse. Why not have a single view somewhere decorated with #require_GET, if you want to control headers, and return an HttpResponse object with the desired payload as application/json?
The fact that your object is a singleton and all other RESTful interactions with it are prohibited suggests that a REST library is the wrong tool for this job.
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)
So basically, I've got a rather large Django project going. It's a private web portal that allows users to manage various phone-related tasks.
Several pages of the portal provide a listing of Model objects to users, and list all of their attributes in a HTML table (so that users can visually look through a list of these items).
The problem I'm having is: I cannot find a Django-ish or pythonic way to handle the sorting of these Model objects by field name. As an example of what I'm talking about, here is one of my views which lists all Partyline Model objects:
def list_partylines(request):
"""
List all `Partyline`s that we own.
"""
# Figure out which sort term to use.
sort_field = request.REQUEST.get('sortby', 'did').strip()
if sort_field.startswith('-'):
search = sort_field[1:]
sort_toggle = ''
else:
search = sort_field
sort_toggle = '-'
# Check to see if the sort term is valid.
if not (search in Partyline._meta.get_all_field_names()):
sort_field = 'did'
if is_user_type(request.user, ['admin']):
partylines = Partyline.objects.all().order_by(sort_field)
else:
partylines = get_my_partylines(request.user, sort_field)
variables = RequestContext(request, {
'partylines': partylines,
'sort_toggle': sort_toggle
})
return render_to_response('portal/partylines/list.html', variables)
The sorting code basically allows users to specify a /url/?sortby=model_field_name parameter which will then return a sorted listing of objects whenever users click on the HTML table name displayed on the page.
Since I have various views in various apps which all show a listing of Model objects, and require sorting, I'm wondering if there is a generic way to do this sorting so that I don't have to?
I'm sorry if this question is a bit unclear, I'm struggling to find the right way to phrase this question.
Thanks.
The way that I'd look at doing this is through a custom QuerySet. In your model, you can define the class QuerySet and add your sorting there. In order to maintain all the logic in the model object, I'd also move the contents of get_my_partylines into the QuerySet, too.
## This class is used to replicate QuerySet methods into a manager.
## This way: Partyline.objects.for_user(foo) works the same as
## Partyline.objects.filter(date=today).for_user(foo)
class CustomQuerySetManager(models.Manager):
def get_query_set(self):
return self.model.QuerySet(self.model)
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)
class Partyline(models.Model):
## Define fields, blah blah.
objects = CustomQuerySetManager()
class QuerySet(QuerySet):
def sort_for_request(self, request):
sort_field = request.REQUEST.get('sortby', 'did').strip()
reverse_order = False
if sort_field.startswith('-'):
search = sort_field[1:]
else:
search = sort_field
reverse_order = True
# Check to see if the sort term is valid.
if not (search in Partyline._meta.get_all_field_names()):
sort_field = 'did'
partylines = self.all().order_by(sort_field)
if reverse_order:
partylines.reverse()
return partylines
def for_user(self, user):
if is_user_type(request.user, ['admin']):
return self.all()
else:
## Code from get_my_partylines goes here.
return self.all() ## Temporary.
views.py:
def list_partylines(request):
"""
List all `Partyline`s that we own.
"""
partylines = Partylines.objects.for_user(request.user).sort_for_request(request)
There's a great example of how this is done in a generic way in django.contrib.admin.views.main.ChangeList although that does much more than sorting you can browse it's code for some hints and ideas. You may also want to look at django.contrib.admin.options.ModelAdmin the changelist method in particular to get more context.