Django model class methods for predefined values - python

I'm working on some Django-code that has a model like this:
class Status(models.Model):
code = models.IntegerField()
text = models.CharField(maxlength=255)
There are about 10 pre-defined code/text-pairs that are stored in the database. Scattered around the codebase I see code like this:
status = Status.objects.get(code=0) # successful
status = Status.objects.get(code=1) # failed
I would rather have a method for each so that the code would look something like this instead:
status = Status.successful()
status = Status.failed()
etc...
Is this possible? I have looked in to the Manager-stuff but I haven't really found a way. Is it time to really RTFM?
In Java it would be a static method and in Ruby you would just define a method on self, but it's not that easy in Python, is it?

You should perhaps implement this by defining a custom manager for your class, and adding two manager methods on that manager (which I believe is the preferred way for adding table-level functionality for any model). However, another way of doing it is by throwing in two class methods on your class that query and return resulting objects, such as:
class Status(models.Model):
code = models.IntegerField()
text = models.CharField(maxlength=255)
#classmethod
def successful(cls):
return cls.objects.get(code=0)
#classmethod
def failed(cls):
return cls.objects.get(code=1)
Do note please that get() is likely to throw different exceptions, such as Status.DoesNotExist and MultipleObjectsReturned.
And for an example implementation of how to do the same thing using Django managers, you could do something like this:
class StatusManager(models.Manager):
def successful(self):
return self.get(code=1)
def failed(self):
return self.get(code=0)
class Status(models.Model):
code = models.IntegerField()
text = models.CharField(maxlength=255)
objects = StatusManager()
Where, you could do Status.objects.successful() and Status.objects.failed() to get what you desire.

Related

How to pass multiple query-set in another class function

Let suppose I have some model
class Testmodel1():
amount = models.IntegerField(null=True)
contact = models.CharField()
class Testmodel2():
price = models.ForeignKey(Testmodel1, null=True)
Now I am using django ORM and apply a query like:
objs = Testmodel2.objects.filter(price__amount=123)
and it is returning n number of obj in any case like (obj1, obj2,..) ,
also I have written a another function in another class which is handling these obj and performing some other task, like-
Class SentMail():
def sending_mail(self, price_obj):
"""performing some task """
SO, currently I am doing like
for obj in objs:
sentmail().sending_mail(obj)
Is there any other better way to do this to make it better, I tried to find a good way, but don't get.
any help would be appreciated.

django: where to put recurring query on list of model objects

I have Product as a model.
I am refactoring some code, and there is a recurring query spread all over the code base which needs replacement.
So I'd like to centralize this query, in order to encapsulate its logic.
I'd like something like
<an_object>.get_uncompleted_products(products);
In this case, preserving existing code, products is already a result of a query (products = Products.objects.filter(filter_expression))
This is just a convenience question, I know a possible answer, but where would you put get_uncompleted_products(), what could be a good"django-way" solution?
I was initially wanting to put it on the Product model. But I was thinking Product methods to work directly on a single model reference, thus the signature would need to be:
class Product(models.Model):
#classmethod
get_uncompleted_products(list)
I am not sure why this gives me the feeling to be not so appropriate. A possible alternative would be to put it into a utility module. I could also have it in the view module but it seems it's used profusely in other views as well so I'd prefer somewhere more generic.
I guess the 'django way' would be to define it as a custom manager method, instead of class method, which can be shared between different models with composition instead of inheritance in case of class methods.
from django.db import models
class ProductQuerySet(models.query.QuerySet):
def get_uncompleted_products(self):
...
class ProductManager(models.Manager):
def get_queryset(self):
return ProductQueryset(self.model, using=self._db)
def get_uncompleted_products(self):
# defined twice to resolve queryset chaining issue with custom managers
return self.get_queryset().get_uncompleted_products()
class Product(models.Model):
...
objects = ProductManager()

Can I add methods and attributes to a class derived from peewee.Model

I am working on an app with Cherrypy and Peewee, and I would like to know if my approach is good or wrong and dangerous.
All the examples of Peewee classes that I found have only the Meta subclass and the <xxx>Field attributes. I have never found an example with helper methods or properties.
I tried to make my own, and it works!
It works very well and I find it convenient, but I am afraid that I'm getting into trouble.
So my question is: can I add as many properties and methods to my classes derived from the peewee.Model, so I can do cool things like in the snippet below?
Or are there limits and guidelines to what I can do?
class PeeweeModel(peewee.Model):
class Meta:
database = db
class TempFile(PeeweeModel):
file_type = peewee.IntegerField()
original_file_name = peewee.CharField()
temp_file_name = peewee.CharField()
DRAWING = 1
PDF = 2
def href(self, settings):
if(self.file_type == DRAWING:
return self.temp_file_name
elif(self.file_type == PDF:
if settings.show_pdf:
return self.temp_file_name
elif settings.show_bitmap:
bitmap = TempFile.create_bitmap_from_pdf(self.temp_file_name)
return bitmap.temp_file_name
#staticmethod
def create_bitmap_from_pdf(self, file_name):
[...]
Edit:
I would appreciate a comment about the constructor.
For example, I would like to do both these:
tmp = TempFile(file_name) # and run my own constructor
tmp = TempFile(original_file_name=file_name, [...]) # and run Peewee's constructor
Yes! Of course you can, by all means. Models are just normal python classes, so you can treat them as such and add methods, properties, attributes, etc.

Django abstract base model with custom QuerySet class

I am using an approach similar to T. Stone's answer on this question. However, I have added an abstract base class, so my models.py looks like this:
class CustomQuerySetManager(models.Manager):
"""A re-usable Manager to access a custom QuerySet"""
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)
def get_query_set(self):
return self.model.QuerySet(self.model)
class MyModel(models.Model):
class Meta:
abstract = True
class QuerySet(QuerySet):
def user(self, pub, *args, **kwargs):
return self.filter(publisher=pub, *args, **kwargs)
...some more methods here
class Book(MyModel):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, related_name='book_author')
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
objects=models.Manager()
obj=CustomQuerySetManager() #for testing purposes only, this will override objects later
This allows me to get all of the books for a given publisher like such:
p = Publisher.object.get(pk=1)
Book.obj.user(p).all()
I would like to extend this so I can define a custom query in the Book model then pass a Q object to the QuerySet class, so the query "publisher=pub" can be different for different models. I still want to be able to call this like Book.obj.user(p).all(). Somewhere in the Book model I need:
pubQ=Q(publisher=pub)
Where can I put this and how do I pass it to QuerySet defined in the Abstract Base Class, while keeping the code as DRY as possible?
That answer is clever, but it breaks the Python principle of "explicit is better than implicit". My first reaction to your code was to tell you that you can't declare a custom queryset inside your model, but I decided to check the mentioned SO answer to see where you got that idea from. Again, it's clever -- not discounting that, but well-written code is self-documenting and should be able to be picked up by any random Django developer and ran with. That's where peer code-reviews come in handy -- had you had one, you'd have instantly got a WTF with that.
The Django core team does it the following way:
class MyQuerySet(models.query.QuerySet):
def some_method(self, an_arg, another_arg, a_kwarg='some_value'):
# do something
return a_queryset
class MyManager(models.Manager):
def get_query_set(self):
return MyQuerySet(self.model)
def some_method(self, *args, **kwargs):
return self.get_query_set().some_method(*args, **kwargs)
It's DRY in the sense that you don't repeat the actual method definition in the manager. But, it's also explicit -- you know exactly what's going on. It's not as DRY as the method you're referencing, but "explicit is better than implicit". Besides if it's done that way in the actual Django codebase, you can be reasonably assured that it's good practice to do so in your own code. And, it has the side-effect of making it much easier to extend and override in subclasses.

Self Referencing Class Definition in python

is there any way to reference a class name from within the class declaration? an example follows:
class Plan(SiloBase):
cost = DataField(int)
start = DataField(System.DateTime)
name = DataField(str)
items = DataCollection(int)
subPlan = ReferenceField(Plan)
i've got a metaclass that reads this information and does some setup, and the base class implements some common saving stuff. i would love to be able to create recursive definitions like this, but so far in my experimentation i have been unable to get the effect i desire, usually running into a "Plan is not defined" error. I understand what is happening, the name of the class isn't in scope inside the class.
Try this:
class Plan(SiloBase):
cost = DataField(int)
start = DataField(System.DateTime)
name = DataField(str)
items = DataCollection(int)
Plan.subPlan = ReferenceField(Plan)
OR use __new__ like this:
class Plan(SiloBase):
def __new__(cls, *args, **kwargs):
cls.cost = DataField(int)
cls.start = DataField(System.DateTime)
cls.name = DataField(str)
cls.items = DataCollection(int)
cls.subPlan = ReferenceField(cls)
return object.__new__(cls, *args, **kwargs)
i've got a metaclass that reads this information and does some setup
Most frameworks that use metaclasses provide a way to resolve this. For instance, Django:
subplan = ForeignKey('self')
Google App Engine:
subplan = SelfReferenceProperty()
The problem with solutions like tacking an additional property on later or using __new__ is that most ORM metaclasses expect the class properties to exist at the time when the class is created.
I understand what is happening, the
name of the class isn't in scope
inside the class.
Not exactly. The name of the class is not yet defined when defining it's contents (e.g. scope).
Sine Python 3.7 and PEP 563 there's a way to do that.
Add the import
from __future__ import annotations
and the following code will work
from __future__ import annotations
from typing import List
class Refer(object):
def __init__(self, x: Plan):
self.x: Plan = x
class Plan(object):
def __init__(self):
pass
subPlan: Refer(Plan())
No, you can't do that. Think about what would happen if you did this:
OtherPlan = Plan
other_plan = OtherPlan()
At instantiation of other_plan, what is the name of the class?
Anyway, this sort of thing is best done in the __new__ method, which takes a cls parameter referring to the class.

Categories