How do class attributes and instance attributes differ in Django models? - python

I have the following models (Here, we are using a class attribute):
class Foo(models.Model):
...
def some_function(self, some_condition):
if some_condition:
# Do something
class Bar(models.Model):
...
foo = models.ForeignKey(Foo)
_some_condition = False # Class attribute
def save(self, *args, **kwargs):
# Do something that sets the `_some_condition` class
# attribute based on some conditions
self.foo.some_function(self._some_condition)
super(Bar, self).save(*args, **kwargs)
In short, my Bar class has a class attribute called _some_condition. This is set in the save function, and then used to call the Foo class's some_function() method.
I can also over-ride the __init__() method and set the _some_condition as an instance attribute. Like so (Here, we are using an instance attribute):
class Bar(models.Model):
...
def __init__(self, *args, **kwargs):
super(Bar, self).__init__(*args, **kwargs)
self._some_condition = False # Instance attribute
...
What even is the difference between the two? I'm generally confused about class and instance attributes in Python.
After doing it both ways for the same model, and testing it in the shell, it seems there is no difference at all between class attributes and instance attributes. Can anyone explain this? The following is the code I used to test both ways:
class MyClass(models.Model):
name = models.CharField(max_length=255)
_class_attr = ''
def __init__(self, *args, **kwargs):
super(MyClass, self).__init__(*args, **kwargs)
self._instance_attr = ''
def save(self, *args, **kwargs):
# Update both before saving
self._class_attr = 'Class attribute'
self._instance_attr = 'Instance attribute'
super(MyClass, self).save(*args, **kwargs)
Results:
>>> from myapp.models import MyClass
>>> MyClass.objects.create(name='first')
<MyClass: MyClass object (1)>
>>> MyClass.objects.create(name='second')
<MyClass: MyClass object (2)>
>>> first = MyClass.objects.get(pk=1)
>>> second = MyClass.objects.get(pk=2)
>>> first._class_attr
''
>>> first._instance_attr
''
>>> second._class_attr
''
>>> second._instance_attr
''
>>> first.save()
>>> first._class_attr
'Class attribute'
>>> first._instance_attr
'Instance attribute'
>>> second._class_attr
''
>>> second._instance_attr
''

Related

How to implement Singleton in Django

I have an object that need to be instantiated ONLY ONCE. Tried using redis for caching the instance failed with error cache.set("some_key", singles, timeout=60*60*24*30) but got serialization error, due the other thread operations:
TypeError: can't pickle _thread.lock objects
But, I can comfortably cache others instances as need.
Thus I am looking for a way to create a Singleton object, I also tried:
class SingletonModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
# self.pk = 1
super(SingletonModel, self).save(*args, **kwargs)
# if self.can_cache:
# self.set_cache()
def delete(self, *args, **kwargs):
pass
class Singleton(SingletonModel):
singles = []
#classmethod
def setSingles(cls, singles):
cls.singles = singles
#classmethod
def loadSingles(cls):
sins = cls.singles
log.warning("*****Found: {} singles".format(len(sins)))
if len(sins) == 0:
sins = cls.doSomeLongOperation()
cls.setSingles(sins)
return sins
In the view.py I call on Singleton.loadSingles() but I notice that I get
Found: 0 singles
after 2-3 requests. Please what is the best way to create Singleton on Djnago without using third party library that might try serialising and persisting the object (which is NOT possible in my case)
I found it easier to use a unique index to accomplish this
class SingletonModel(models.Model):
_singleton = models.BooleanField(default=True, editable=False, unique=True)
class Meta:
abstract = True
This is my Singleton Abstract Model.
class SingletonModel(models.Model):
"""Singleton Django Model"""
class Meta:
abstract = True
def save(self, *args, **kwargs):
"""
Save object to the database. Removes all other entries if there
are any.
"""
self.__class__.objects.exclude(id=self.id).delete()
super(SingletonModel, self).save(*args, **kwargs)
#classmethod
def load(cls):
"""
Load object from the database. Failing that, create a new empty
(default) instance of the object and return it (without saving it
to the database).
"""
try:
return cls.objects.get()
except cls.DoesNotExist:
return cls()
The code below simply prevents the creation of a new instance of the Revenue model if one exists. I believe this should point you in the right direction.
Best of luck !!!
class RevenueWallet(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Meta:
verbose_name = "Revenue"
def save(self, *args, **kwargs):
"""
:param args:
:param kwargs:
:return:
"""
# Checking if pk exists so that updates can be saved
if not RevenueWallet.objects.filter(pk=self.pk).exists() and RevenueWallet.objects.exists():
raise ValidationError('There can be only one instance of this model')
return super(RevenueWallet, self).save(*args, **kwargs)

Creating a dynamic class that inherit class it is made in

When I try to create a class from within a parent class, such that the child class inherits 'self', I get the following error:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class A(object):
create_proxy = False
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
if self.create_proxy:
class Proxy(SomeMixin, self):
pass
[...]
I'm sure this is somehow possible; any suggestions?
Make the following change
...
if self.create_proxy:
class Proxy(SomeMixin, A): #use class name instead of self
pass
...
Also make sure that SomeMixin is a subclass of object, otherwise it will result it metaclass conflict.
class SomeMixin(object):
pass
class A(object):
create_proxy = True #False
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
if self.create_proxy:
class Proxy(SomeMixin, A):
pass
a = A() #test

Django override model subclass create method

I'm looking for right ways to override the create() method of a subclass.
Basically I'm doing this:
class Base(models.Model):
field1_base = models.IntegerField()
def __init__(self, field1_base):
# LOGICS
self.field1_base = field1_base
class A(Base):
field2_sub = models.IntegerField()
def __init__(self, field2_sub, field1_base):
# LOGICS
self.field2_sub = field2_sub
super(A, self).__init__(field1_base)
A(field2_sub=1, field1_base=2)
However we can't override the __init__() method of a model.
I just want to leave some fields to be assigned in base class's methods.
Is there a proper way to do this using create() method?
Of course I can custom create method of Base class, however what I want is to invoke Base.create and A.create at the same time on the creation of A, which makes the situation different from this question
I would do something like this.
class Base(models.Model):
field1_base = models.IntegerField()
def initialize(self, *args, **kwargs):
self.field1_base = kwargs['field1_base']
#classmethod
def create(cls, *args, **kwargs):
# LOGICS
self = cls()
self.initialize(*args, **kwargs)
return self
class A(Base):
field2_sub = models.IntegerField()
def initialize(self, *args, **kwargs):
super(A, self).initialize(*args, **kwargs)
self.field2_sub = kwargs['field1_base']
A.create(field2_sub=1, field1_base=2)

How does Django display a <bound method ... >> object as a string object in django-admin?

How does Django translate this <bound method Child.parent_identity of <Child: >> object in a string object, and displays it as such in my django-admin "inline" Child class idparent field ?
What does Django do ?
I have the following application structure:
##========================models.py
...
from django_extensions.db.fields import UUIDField
class Parent(models.Model):
id = UUIDField(primary_key=True)
class Child(models.Model):
parent = models.ForeignKey(Parent)
idparent = models.CharField(max_length=100)
def parent_identity(self):
return self.parent_id
#========================admin.py
class ChildForm(forms.ModelForm):
class Meta:
model = Child
exclude = []
def __init__(self, *args, **kwargs):
super(ChildForm, self).__init__(*args, **kwargs)
#print self.instance.parent_identity
self.initial['idparent'] = self.instance.parent_identity
class ChildInline(admin.TabularInline):
model = Child
extra = 1
form = ChildForm
class ParentAdmin(admin.ModelAdmin):
exclude = []
inlines = [ChildInline]
#list_display, etc
admin.site.register(Parent,ParentAdmin)
My inline idparent field displays the Parent id field CORRECTLY in the admin inline interface. Being a newbie, it's magic for me, because self.instance.parent_identity is initially not a string object.
print self.instance.parent_identity
#it prints : <bound method Child.parent_identity of <Child: >>
But how to explictly print the string content as follows
>>print self.instance.parent_identity
#would print : fffeee29-7ac6-42eb-8a8d-eb212d2365ff
That is, how to get it so as to deal with it in the ChildForm class ?
UPDATE
I do not mind specifically about "UUID in the form when the instance hasn't been created yet"
and i do not want to provide an initial value myself.
I want my still empty (extra) Child fields (one field in my example code: idparent) to contain by default something which is Parent variable.
Is it possible ?
Django templates automatically call any object that is callable; e.g. the callable() function returns True when you pass the object in. From the Variables section in the template documentation:
If the resulting value is callable, it is called with no arguments. The result of the call becomes the template value.
Bound methods are callable, so instead of using self.instance.parent_identity, the template uses the output of self.instance.parent_identity().
In your own code, you generally already know that something is a method and you call it explicitly:
def __init__(self, *args, **kwargs):
super(ChildForm, self).__init__(*args, **kwargs)
self.initial['idparent'] = self.instance.parent_identity()
You can treat the parent_identity method as an attribute; have Python call it automatically without you having to call it explicitly. If you never have to pass in an argument, then that might make sense. You do this by decorating the method with the #property decorator:
class Child(models.Model):
parent = models.ForeignKey(Parent)
idparent = models.CharField(max_length=100)
#property
def parent_identity(self):
return self.parent_id
at which point self.instance.parent_identity will give you the return value of that method.
Take into account that the UUIDField only is given a value on pre-save; it'll be None until the object is saved in a database.
If you really wanted to UUID in the form when the instance hasn't been created yet, you'll have to provide an initial value yourself:
import uuid
class ParentAdmin(admin.ModelAdmin):
exclude = []
inlines = [ChildInline]
def __init__(self, *args, **kwargs):
super(ParentAdmin, self).__init__(*args, **kwargs)
self.fields['id'].initial = uuid.uuid4
You are calling a function, which means you need to use it as such:
self.initial['idparent'] = self.instance.parent_identity()
Alternately you could wrap it with the #property decorator and continue using it as you are, notice that you need to use self.parent.id if you want to access the parent's id:
class Child(models.Model):
parent = models.ForeignKey(Parent)
idparent = models.CharField(max_length=100)
#property
def parent_identity(self):
return self.parent.id

Python Accessing to the parent class (Not inheritance)

Is possible access to the parent methods/properties in a class that are inside of the other class?
class ClassA:
a = 'a'
class ClassB():
def method(self):
return self.a
instance = ClassA()
instance2 = instance.ClassB()
instance2.method()
No, nesting a class doesn't automatically produce a relationship between instances. All you did was create an attribute on ClassA that happens to be a class object. Calling that attribute on instances just finds the class attribute and a new instance of ClassB is created without any knowledge of or reference to the ClassA instance.
You'll need to make such relationships explicit by passing in a reference:
class ClassB():
def __init__(self, a):
self.a = a
def method(self):
return self.a
class ClassA:
a = 'a'
def class_b_factory(self):
return ClassB(self)
instance = ClassA()
instance2 = instance.class_b_factory()
instance2.method()

Categories