How to create mixin to use #property in classes - python

I've got the following class
class UserProfile(models.Model):
# ...
#property
def get_fields(self):
return [(field.name, field.value_to_string(self)) for field in UserProfile._meta.fields]
I want to use that property on all my classes. So I want to make a mixin.
class HelperMixin(object):
#property
def get_fields(self):
return [(field.name, field.value_to_string(self)) for field in UserProfile._meta.fields]
Trouble is, the property has class specific code in it. How do I generalize the code to work in other classes?
And what is the difference when I use it like this:
class UserProfile(models.Model, HelperMixin)
vs. like this
class UserProfile(HelperMixin, models.Model)

Use self._meta.fields, class members are available on instances as well:
class HelperMixin(object):
#property
def get_fields(self):
return [(field.name, field.value_to_string(self)) for field in self._meta.fields]
The differense between class UserProfile(models.Model, HelperMixin) and class UserProfile(HelperMixin, models.Model) lies in the MRO (method resolution order). It would seem natural to have the mixin before the base class, but as long as the base and the mixin does not have members with the same name it makes no difference.

Related

Django-carton work with two Product models

I am using Django-carton (https://github.com/lazybird/django-carton) and I wonder if it is possible to associate more than one model as a Product model.
Right now, I have a base class Product which I registered as my product model and I inherited Pub and Restaurant from it. The problem I face is, cart.show() basically returns all product objects in the cart which only have products attributes and not Pub's or Restaurant's, and I can not distinguish which one is Pub and which one is Restaurant.
In your base model you can define method get_type without implementation, and in inherited classes, implement it, for example:
class Product(models.Model):
...
def get_type(self):
pass
class Meta:
...
abstract = True
class Pub(Product):
...
def get_type(self):
return 'pub'
class Restaurant(Product):
...
def get_type(self):
return 'restaurant'
If you know the type of an instance, you can easily access needed attributes.
Or you can use just isinstance built-in function:
if isinstance(obj, Pub):
# Your logic here

Add dynamic property with class in Python metaclass

I use mongoengine with django rest framework. My model:
import mongoengine as mongo
class Plan(mongo.Document):
slug = mongo.StringField(max_length=255, primary_key=True)
subplans = mongo.ListField(mongo.EmbeddedDocumentField('self'))
I'm need serializer that be looks like this:
class PlanSerializer(serializers.DocumentSerializer):
subplans = PlanSerializer(many=True, required=False)
class Meta:
model = Plan
But that incorrect for Python. So I use metaclass for adding subplans field dynamicly:
class AddSubplanAttrMetaclass(type):
def __new__(cls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = globals()[name]
dct['subplans'] = class_obj(many=True, required=False)
return super(AddSubplanAttrMetaclass, cls).__new__(cls, name, bases, dct)
class PlanSerializer(serializers.DocumentSerializer, metaclass=AddSubplanAttrMetaclass):
class Meta:
model = Plan
How I can set PlanSerializer class to property inside __new__ method of metaclass?
The problem you have there is that when you try to either use the line
subplans = PlanSerializer(many=True, required=False) and when trying with the metaclass, the line class_obj = globals()[name] when your PlanSerializerclass itself was not defined yet. (Check my answer at How is super() in Python 3 implemented?)
The correct way to do that in the metaclass would be to call the superclass's new first - that returns you the actual class object, and then call that object - something along:
class AddSubplanAttrMetaclass(type):
def __new__(metacls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = super(AddSubplanAttrMetaclass, cls).__new__(metacls, name, bases, dct)
class_obj.subplans = class_obj(many=True, required=False)
return class_obj
But that is both not needed, and might still have issues - as not all the class initialization is completed while you are still inside the metaclass's __new__ (or even __init__) methods. For example, if the __init__ method of PlanSerializer itself would make use of super, that call would fail - super can only be used after class has been fully initialized.
However, you don't need a metaclass at all for that - you probably can simply set the subplans attribute as a descriptor - and retrieve the attribute lazily.
class PlanSerializer(serializers.DocumentSerializer):
class Meta:
model = Plan
PlanSerializer.subplans = PlanSerializer(many=True, required=False)
I said probably because this won't work if Mongo needs the attribute to be set when initializing the class itself - if that is the case, you can try resorting to a descriptor object. A descriptor is simply an object that implements the __get__ method, like below. That is usually done with the #property decorator, but that would not work for class level attributes, which you need for this case.
class PlanSerializer(serializers.DocumentSerializer):
class Subplans(object):
serializer = None
def __get__(self, instance, owner):
if not self.serializer:
self.serializer = PlanSerializer(many=True, required=False)
return self.serializer
subplans = Subplans()
class Meta:
model = Plan
In that way the usage of the call to the Subplans class is delayed to when it is actually used, instead of the time of parsing the class body, and it should work.

Django: access the model meta class value

I have some model classes defined:
class ModelA(models.Model):
class Meta:
abstract = True
class ModelB(ModelA):
class Meta:
abstract = False
So, now I have a class object, I want to check if it is abstract, is there any way to do this?
For example, I want something like:
>>> ModelA.abstract
True
>>> ModelB.abstract
False
Oh, I found that it is easy to get the Meta class by _meta field of the class:
>>> ModelA._meta.abstract
True
technically, strictly speaking, no external method should access a method or property that begins with an underscore (_) as it is private and protected from outsider access. To solve this, in your model file, add a property:
class Foo(models.Model):
class Meta:
verbose_name = "Foolish"
#property
def verbose_name(self):
return self._meta.verbose_name
Then your view can "properly" access the Meta.verbose_name via Foo().verbose_name as opposed to Foo._meta.verbose_name

Creating parent model that all models will inherit from in Django

I am attempting to create a model class that all models will inherit from when they are created (in Django). I want any model class, with any attributes to be able to inherit from this class and read from the appropriate database table.
I know I am going need to use **kwargs and .setattr() at some point but am unclear as to where I even start. I am also going to try to recreate .all(), .filter() and .get() with in that class that all other methods that inherit this class can access.
This is what I have so far:
import sqlite3
class Model:
def __init__(self):
pass
#classmethod
def all(self, **kwargs):
pass
#classmethod
def get(self):
pass
#classmethod
def filter(self):
pass
###don't touch the code for these
class Users(Model):
pass
class Stocks(Model):
pass
How can I go about the initialization of this class?
It looks like you're trying to insert an abstract base class for your models.
Basically, what you've got there is correct, except you're missing
from django.db.models import Model
class MyModelBase(Model):
class Meta:
abstract = True
# ... The fields and methods I want all my models to inherit.
Then rather than making your models inherit from django.db.models.Model, they should inherit from MyModelBase.

django generic one-to-one relations, with cascading deletes

I'm trying to emulate an inheritance model using django's generic relations. So far, this is what I've come up with:
class Base(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
... other stuff
class Meta:
unique_together = ("content_type", "object_id")
class SubClass1(models.Model):
... properties for this class
_base = generic.GenericRelation(Base)
#property
def base(self):
return self._base.all()[0]
From what you can see here (hopefully, at least), the SubClass1 should have a one-to-one relationship with Base, which is why I went through all the work of creating that _base field and then covering it with the base property. Having that generic relation will automagically wire up cascading deletes, which is what I want (and I have a post-delete signal that will wire up the delete from the other direction).
There could be any number of subclasses off of Base, and the thing that makes me feel gross is having to copy&paste the _base and its covering property. I've also got a custom object manager that goes with this, and potentially other things that essentially should all behave the same within each subclass of Base.
Is there a good way to go about encapsulating this common functionality so I don't have to copy&paste it for each sub class?
Why not have a BaseSubClass, and then inherit from that. So based on your example:
class BaseSubClass(models.Model):
_base = generic.GenericRelation(Base)
class Meta:
abstract = True
#property
def base(self):
return self._base.all()[0]
class SubClass1(BaseSubClass):
# fields

Categories