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

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())

Related

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: Can I use objects.filter() for generic foreignkey?

symbol.py
class Symbol(BaseModel):
name = models.CharField(max_length=30,)
class Meta:
abstract = True
class StockSymbol(Symbol):
market = models.CharField(max_length=10,)
my_daily_price = GenericRelation(MyDailyPrice)
daily_price.py
class DailyPrice(BaseModel):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
abstract = True
class MyDailyPrice(DailyPrice):
open = models.DecimalField(
max_digits=15,
decimal_places=2,
)
What I want to do is,
symbol = StockSymbol.objects.first()
MyDailyPrice.objects.filter(content_object=symbol)
But it occured errors:
FieldError: Field 'content_object' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
StockSymbol already has GenericRelation. What's wrong with it?
Or do I have to override ojbect manager?
You can filter with content_type and object_id, instead of content_object.
from django.contrib.admin.options import get_content_type_for_model
symbol = StockSymbol.objects.first()
MyDailyPrice.objects.filter(content_type=get_content_type_for_model(symbol), object_id=symbol.pk)
I wrapped up #Akash's answer in a method to be added to a custom Manager or QuerySet:
def gfks(self, **kwargs):
filters = {}
for field, obj in kwargs.items():
gfk = self.model._meta.get_field(field)
filters[gfk.ct_field] = ContentType.objects.get_for_model( obj )
filters[gfk.fk_field] = obj.pk
return self.filter(**filters)
For example, if you had a model called Comparison with two GFKs called product1 and product2, and added this method, usage would look like:
comp = Comparison.objects.gfks(product1=foo, product2=bar)
Would be nice if Django's contenttypes app provided some similar sugar automatically, but I'll settle for adding this to my BaseQuerySet class in the meantime.

Relationships with pairs of interrelated models

Let's say I have 3 models in Django: Person, Workfield and SubWorkfield.
A person can have many workfield-s and many subWorkfield-s as well, but the subWorkfield-s must be related to their parent workfield-s ( which the person must be related to).
So how do I enforce that whenever a person is related to a subWorkfield then he must also be related to that subWorkfield's parent workfield?
Here's what I have so far, but I don't think it enforces the relationship:
class Person(models.Model):
name = models.CharField(max_length=200)
workfield = models.ManyToManyField(Workfield)
subworkfield = models.ManyToManyField(SubWorkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
I need to have the workfields and subWorkfield-s decoupled because a person can belong to a workfield without any subWorkfield-s.
There really isn't any good way to do what you want purely with the table structure itself. However, if you do the following:
class Person(models.Model):
name = models.CharField(max_length=200)
workfields = models.ManyToManyField(Workfield)
subworkfields = models.ManyToManyField(SubWorkfield)
def add_subworkfield(self, subworkfield):
if subworkfield.workfield not in self.workfields:
return False
else:
self.subworkfields.append(subworkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
And then you will just use the add_subworkfield method when adding subworkfields to Persons.
Alternatively, inside of the Person model you could override the save method:
class Person(models.Model):
...
def save(self, *args, **kwargs):
for subworkfield in self.subworkfields:
if subworkfield.workfield not in self.workfields:
return
super(Person, self).save(*args, **kwargs)
If it's a small app and it's just you, the first way should suffice. However, if you're working on something larger with multiple people, overriding save would be a safety incase someone doesn't use the add_subworkfield method.

Default filter in Django model

Is is possible to set a default filter in Django models?
Something like:
class MyModel(models.Model):
timestamp = models.DateTimeField(default=datetime.utcnow)
active = models.BooleanField(default=True)
class Meta:
ordering = ['-timestamp']
filtering = [active=True]
You'll have to override the manager:
class MyModelManager(models.Manager):
def get_queryset(self):
return super(MyModelManager, self).get_queryset().filter(active=True)
class MyModel(models.Model):
timestamp = models.DateTimeField(default=datetime.utcnow)
active = models.BooleanField(default=True)
objects = MyModelManager()
class Meta:
ordering = ['-timestamp']
get_queryset was get_query_set before Django 1.6
I was able to accomplish what I wanted by adding managers to the model. I also created an abstract base class to make it easier to add this to other models without having to replicate the same code - here is the modified example:
class MyActiveManager(models.Manager):
def get_queryset(self):
return super(MyModelManager, self).get_queryset().filter(active=True)
class MyInactiveManager(models.Manager):
def get_queryset(self):
return super(MyModelManager, self).get_queryset().filter(active=False)
class AbstractModel(models.Model):
timestamp = models.DateTimeField(default=datetime.utcnow)
active = models.BooleanField(default=True)
objects = MyActiveManager()
objects_inactive = MyInactiveManager()
objects_all = models.Manager()
class Meta:
abstract = True
ordering = ['-timestamp']
class MyModel(AbstractModel):
# Define active-enabled model here
Now, any model I want to have an "active" (and "timestamp" in this example) field can just inherit from the base model. When I use MyModel.objects.all() - I get all objects with active=True - this is especially useful if I already have alot of code using the objects manager. If I want only inactive results, I use MyModel.objects_inactive.all(), and if I want all records regardless of the value of active, I use MyModel.objects_all.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')

Categories