Django: How to set a property in a model using #property - python

I'm trying to assign two properties to my User class. The first assigned property will be used in assigning the second property. Is this correct? I'm using the #property decorator...
class User(n):
group = models.ForeignKey(Brand, null=True, blank=True)
is_admin = models.BooleanField(default=True)
# assign the apps property (User.apps)
#property
def assign_apps(self):
self.apps = get_user_apps(self.group, self.is_admin)
# with User.apps, assign the apps_meta property (User.apps_meta)
#property
def assign_apps_meta(self):
self.apps_meta = get_user_apps_meta(self.apps)

I still don't understand why you want to use properties here. This really isn't what they are for. The property decorator is for creating getters and setters for complex attributes; that's not what you're doing at all.
This seems like a job for a simple method.
def assign_apps(self):
self.apps = get_user_apps(self.group, self.is_admin)
self.apps_meta = get_user_apps_meta(self.apps)
and now you can call my_user.assign_apps().

if you're writing method that returns some value or string then you should use
#property decorator
class Person(models.Model):
name = models.CharField(max_length=255)
birth_date = models.DateField()
#property
def age(self):
return ......

Related

Django, Assign specific fuction tomodel field and then call it

I have a model built like this
class ApiPartner(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30, verbose_name=_('Name'))
api_key = models.CharField(max_length=50, verbose_name=_('API key'), null=True)
secret_key = models.CharField(max_length=50, verbose_name=_('Secret key'), null=True)
client_key = models.CharField(max_length=50, verbose_name=_('Client key'), null=True)
endpoint = models.CharField(max_length=50, verbose_name=_('Endpoint'), null=True)
logo = models.ImageField(upload_to='logos/', null=True)
evey API partner has its own method to retrieve data, for example
def get_youtube_posts(endpoint,api_key):
results=list(requests.get(endpoint+'?key='+api_key).json())
return results[0:50]
def get_instagram_posts(endpoint,api_key,secret_key):
return requests.get(endpoint+'?key='+api_key+'&secret='+secret_key)
the question is: how do i assign the 'get_posts' function to the model so i can call a generic ApiPartner.get_posts() and it will retrieve the posts using the given function?
I'm thinking about like a models.FunctionField but i know that doesn't exist.
I think this is more a logical problem than a technical one but i can't find a way. Thank you
Maybe I'm understanding the question wrong; but you can just assign it as a property on the model class:
class MyModel(models.Model):
fields...
#property
def youtube_posts(self):
results=list(requests.get(self.endpoint+'?key='+self.api_key).json())
return results[0:50]
#property
def instagram_posts(self):
return requests.get(self.endpoint+'?key='+self.api_key+'&secret='+self.secret_key)
Then you can call it with the instance of your model.
mymodel = MyModel.objects.all().first()
youtube_posts = mymodel.youtube_posts
# or
instagram_posts = mymodel.instagram_posts
But this will only return one or the other since your models are based on one specific endpoint.
To create a more generic method on the model, use the above methods, plus this:
#property
def platform_posts(self)
if "instagram" in self.endpoint:
return self.instagram_posts
elif "youtube" in self.endpoint:
return self.youtube_posts
... You get the gist.

Change representation of Superclass without field depending of Subclass object (__str__)

I am running a django app and have a setup like this:
ModelSuper(models.Model):
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name
So ModelForeign has a FK to ModelSuper. What happens now is that when I create an instance of ModelForeign I can choose if it belongs either to ModelSub1 or to ModelSub2. But the string representation is ModelSuper Onject (3) where (3) is the id.
Normally I can change this representation by overwriting the __str__ method on the model, but since I do not have any fields on the Supermodel I can't return anything.
What I tried:
I have already implemented the __str__ method in the Submodels but that does not help.
I wanted to make the Super model abstract. But this does not let me point FKs to the Supermodel, so I can't do this. My setup requires this FK
I used a generic FK with django's ContentType framework. This is also not an option because it messes completely with my app and is also not recommended from an SQL perspective.
Also when I do API-calls I get ModelSuper Onject (3) back instead of a human-readable name.
Is there a way to do what I intend to do? Thanks in advance for help and hints. Very much appreciated!
EDIT1: What I tried thanks to Abdul's help:
class ModelA(models.Model):
class Meta:
abstract = False
TYPE_CHOICES = [('sub1', 'sub1'), ('sub2', 'sub2')]
type_model = models.CharField(max_length=50, choices=TYPE_CHOICES, null=True, blank=True)
def __str__(self):
if self.type_model == "sub1":
return "sub1"
elif self.type_model == "sub2":
return "sub2"
else:
return "unkown"
I am not understanding how your foreign key works as model inheritance means the tables are separate. How about trying something like this:-
ModelA(models.Model):
TYPE_CHOICES = [('Sub1', 'ModelSub1'), ('Sub2', 'ModelSub2')]
model_type = models.CharField(max_length=4, choices=TYPE_CHOICES)
def __str__:
# Return string representation using if-else
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name

django - a model field which gets the result of model method

I have this model
class Home(models.Model):
...
number_female = models.IntegerField()
number_male = models.IntegerField()
def all_people(self):
return self.number_female + self.number_male
all_members = all_people(self)
i am getting: name 'self' is not defined.
how can I define a field which gets the result of models method? this Home scenario is just an example, i have more complex models, i just wanted to make question clearer.
If you would like to add a calculated field like all_members as a part of your model, then you will have to override the save function:
class Home(models.Model):
...
all_members = models.IntegerField()
def save(self):
all_members = self.all_people()
super(Home, self).save()
Now you can filter by all_members. It would be better to use the #property decorator for all_members, in this case.
Another approach would be to use Django's extra method as mentioned in a different stackoverflow answer
You still need to define all_members as a model field (not as an integer), and then populate it with the desired value when you save() the instance.
class Home(models.Model):
...
number_female = models.IntegerField()
number_male = models.IntegerField()
all_members = models.IntegerField()
def save(self):
self.all_members = self.number_female + self.number_male
super(Home, self).save()
I think Django Managers can be a solution here. Example:
Custom Manager:
class CustomFilter(models.Manager):
def all_people(self):
return self.number_female + self.number_male
Model:
class Home(models.Model):
....
objects= CustomFilter()
Views:
allpeople= Home.objects.all_people(Home.objects.all())

How to store functions in django models

edit: I completely rewrote the question as the original one didn't clearly explain my question
I want to run a function which is specific to each particular model instance.
Ideally I want something like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.FunctionField() #stores a function specific to this instance
x = MyModel(data='originalx', perform_unique_action=func_for_x)
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModel(data='originaly', perform_unique_action=func_for_y)
y.perform_unique_action() #will do whatever is specified for instance y
However there is no datatype FunctionField. Normally this would be solvable with inheritance, and creating subclasses of MyModel, maybe like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class MyModelX(MyModel):
perform_unique_action = function_X
class MyModelY(MyModel):
perform_unique_action = function_Y
x = MyModelX(data='originalx')
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModelY(data='originaly')
y.perform_unique_action() #will do whatever is specified for instance y
Unfortunately, I don't think I can use inheritance because I am trying to access the function this way:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel = SecondModel.objects.get(other_data=3)
secondmodel.mymodel.perform_unique_action()
The problem seems to be that I don't know what type the foreign key is going to be in SecondModel if I override the perform_unique_action in subclasses.
Can I access MyModel from SecondModel as a foreign key and still have a unique function for each instance of MyModel?
This works for me. I haven't tested it, but you should be able to create another class and override their methods and it'll work. Check the class Meta line, it'll treat it as an abstract class. Here's an example of my actual classes that I'm working on right now.
EDIT: Added VoteComment class and tested it. It works as expected!
class Vote(models.Model):
VOTE_ENUM = (
(VoteEnum.DOWN_VOTE, VoteEnum.toString(VoteEnum.DOWN_VOTE)),
(VoteEnum.NONE, VoteEnum.toString(VoteEnum.NONE)),
(VoteEnum.UP_VOTE, VoteEnum.toString(VoteEnum.UP_VOTE)),
)
question = models.ForeignKey(Question, null=False, editable=False, blank=False)
voter = models.ForeignKey(User, blank=False, null=False, editable=False)
vote_type = models.SmallIntegerField(default=0, null=False, blank=False, choices=VOTE_ENUM)
class Meta:
abstract = True
def is_upvote(self):
return self.vote_type > 0
def is_downvote(self):
return self.vote_type < 0
class VoteAnswer(Vote):
answer = models.ForeignKey(Answer, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "answer"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, answer.text[:32])
def is_upvote(self):
return "FOO! "+str(super(VoteAnswer, self).is_upvote())
class VoteComment(Vote):
comment = models.ForeignKey(Comment, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "comment"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, comment.text[:32])
def is_upvote(self):
return "BAR!"
I came up with two ways of having a specific function defined for each object. One was using marshal to create bytecode which can be stored in the database (not a good way), and the other was by storing a reference to the function to be run, as suggested by Randall. Here is my solution using a stored reference:
class MyModel(models.Model):
data = models.CharField(max_length=100)
action_module = models.CharField(max_length=100)
action_function = models.CharField(max_length=100)
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel_obj = SecondModel.objects.get(other_data=3)
#The goal is to run a function specific to the instance
#of MyModel referred to in secondmodel_obj
module_name = secondmodel_obj.mymodel.action_module
func_name = secondmodel_obj.mymodel.action_function
module = __import__(module_name)
func = vars(module)[func_name]
func()
Thanks to everyone who replied, I couldn't have got to this answer if it weren't for your help.
You could achive some similar behavior overriding the save method. And providing special callbacks to your instances.
Something like:
def default_function(instance):
#do something with the model instance
class ParentModel(model.Model):
data = models.CharField()
callback_function = default_function
def save(self, *args, **kwargs):
if hasattr(self, 'callback_function'):
self.callback_function(self)
super(ParentModel, self).save(*args, **kwargs)
class ChildModel():
different_data = models.CharField()
callback_function = other_fun_specific_to_this_model
instance = ChildModel()
#Specific function to this particular instance
instance.callback_function = lambda inst: print inst.different_data
instance.save()
You can write endpoints on your server and limit their access to just your self. Then store in each model instance corresponding url. For example:
views.py
def funx_x(request):
pass
def func_y(request):
pass
models.py:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.URLField()
and then:
x = MyModel(data='originalx', perform_unique_action='http://localhost/funx_x')
requests.post(x.perform_unique_action)
i dont know whether i understand u correct or not. but you can check out this example here.
Example:
A string representing an attribute on the model. This behaves almost the same as the callable, but self in this context is the model instance. Here's a full model example:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'decade_born_in')

subclassing models.Manager

I see no difference in sub classing the models.manager object and overriding the get_query_set method or simply creating a new method in the sub class and using the method. For the reason being I have taken example from the django book;
class MaleManager(models.Manager):
def get_query_set(self):
return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager):
def get_query_set(self):
return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
With this I could use; Person.women.all() or Person.men.all() to fetch all the men or women model object. But, I guess the similar thing can be achieved without overriding the get_query_set method by simply doing;
class MaleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
class FemaleManager(models.Manager):
def get_female(self):
return return self.filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
Now, with this, I can fetch all those objects with a little technique like;
Person.objects.get_male() or Person.objects.get_female(). But, there's no any subtle difference between how I could fetch the objects yet there is difference in terms of readability and use in first case while the second one is much easier to understand and has lesser code.Do they make some significant difference in coding and patterns ? The other thing with the second one, what if I place both the methods inside a same class like;
class PeopleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
def get_female(self):
return return self.filter(sex='F')
Usually you don't want several managers for the model. It's better to extend default manager.
class PeopleManager(models.Manager):
def get_male(self):
return self.filter(sex='M')
def get_female(self):
return return self.filter(sex='F')
class Person(models.Model):
....
objects = PeopleManager()
Then you will be able to use Person.objects.get_male(), Person.objects.get_female() and built-in methods like Person.objects.order_by(). You can look at custom managers in django.contrib.auth.models for example.
get_query_set is good for inheritance. For example you can define
class SmithManager(PeopleManager):
def get_queryset(self):
return super(SmithManager, self).get_query_set().filter(last_name='Smith')
and all the methods of the manager will return only Smiths, (Person.objects.get_male() will return only males named Smith and so on). And you don't need to rewrite all the methods.

Categories