models.py
from django.db import models
from alpha_id import get_alpha_id
class Sample(models.Model):
alpha_id = get_alpha_id(self.id)
sample_name = models.CharField(max_length=30)
entry_date = models.DateField(auto_now_add=True)
def __unicode__(self):
return self.alpha_id
alpha_id.py
import string
ALL_LETTERS = string.ascii_uppercase.replace('F', '').replace('I', '').replace('L', '').replace('O', '').replace('V', '')
def get_alpha_id(id):
""" Return the alpha numeric ID according to the current
integer id.
"""
global ALL_LETTERS
alpha = ALL_LETTERS[(id%len(ALL_LETTERS))-1]
return str(id) + '_' + alpha
Here, I am trying to create a alpha_id model attribute which establishes an alpha numeric id based on the automatically created integer id attribute. I wrote a function that performs the algorithm, and I just need to send that method the id of the current instantiated model. For example:
>>> get_alpha_id(1)
1_A
>>>get_alpha_id(2)
2_B
Anyways I have that logic all figured out. All i need to do is figure out how to pass to that function the id attribute of the current instantiation of my Sample model.
Obviously my problem here is that I am not referring to an instantiation of the class Sample, so the use of "self.id" is causing an error. To be specific:
alpha_id = get_alpha_id(self.id)
NameError: name 'self' is not defined
I have a feeling the solution involves something to do with defining an __init__method but I am not quite sure how I would go about doing it. I have looked at the Model.py base class and I couldn't quite find where the id attribute is defined.
To sum it up, how can I access the current id of an instantiated django model so that I can use that integer value to inform the creation of another attribute?
Instead of making alpha_id a class attribute, you need to make it an instance attribute using the #property decorator on an instance method:
class Sample(models.Model):
sample_name = models.CharField(max_length=30)
entry_date = models.DateField(auto_now_add=True)
#property
def alpha_id(self):
return get_alpha_id(self.id)
def __unicode__(self):
return self.alpha_id
Related
I have the following models example:
class TestSet(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Class(models.Model):
name = models.CharField(max_length=50)
test_set_id = models.ForeignKey(TestSet)
def __str__(self):
return self.name
class Test(models.Model):
title = models.CharField(max_length=50)
test_set_id = models.ForeignKey(TestSet)
def __str__(self):
return self.title
class ClassTest(models.Model):
class_id = models.ForeignKey(Class)
test_id = models.ForeignKey(TestSet)
memo = models.CharField(max_length=250)
def __str__(self):
return self.memo
What I want to do is when a new Class or Test is created, it should also create automatically a ClassTest entry if their test_set_id matches with the currenct entries on the database.
Example:
Lets say I already have three Test objects with test_set_id = 1
I create a new Class object with test_set_id = 1
After creating the new Class object, the program should also create 3 new entries in the ClassTest model connecting classes to tests based on the match of the test_set_id field.
It should work the same if a new Test object is added.
So there are two answers that comes in mind:
You can use Django Signals to create a receiver that execute a specific command when a Class object is created. Something around the code below. Consider that the instance is basically the just created class and you can simply operate standard django query with that do get and create additional objects.
#receiver(post_save, sender=Class)
def create_test_sets(sender, instance, created, **kwargs):
if created:
///do things///
On a second beat thought, I would ask what's the overall use-case your are trying to solve. I feel there is some redundancy here but I can't fully figure out where unless you help me gather the bigger picture.
I want to annotate additional field to my model via custom queryset and access it with a function, here is a simplified code:
class CustomQuerySet(models.QuerySet):
def my_func(self):
return self.annotate(b_count=self.b_set.count()) # 'CustomQuerySet' object has no attribute 'b_set'
class A(models.Model):
objects = CustomQuerySet.as_manager()
class B(models.Model):
a = models.ForeignKey(A)
I want to access it like this models.A.objects.my_func().all(), so an extra field would be added to my model when I want to get it.
But I can't access b_set from a CustomQuerySet.
Previously I was using a #property in model A, but it makes an additional DB request every time.
How could I access a set of related model from a Custom QuerySet?
Probably, you should take a look at the Manager example that does exactly what you want: https://docs.djangoproject.com/en/3.2/topics/db/managers/#adding-extra-manager-methods
from django.db import models
from django.db.models.functions import Coalesce
class PollManager(models.Manager):
def with_counts(self):
return self.annotate(
num_responses=Coalesce(models.Count("response"), 0)
)
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
# ...
In your example, I think you can't access b_set because this attribute belongs to a model instance, not to the queryset itself.
I am using PyCharm 4.5.2, Django 1.8.2.
If I define a class as:
class User(models.Model):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
slug = models.SlugField(max_length=256, unique=True, default=make_slug)
def make_slug(self):
return self.first_name + self.last_name[0]
The IDE highlights default=make_slug with make_slug being undefined. The interpretter agrees and when the development server tries to refresh it exits with status 1 and the error NameError: name 'make_slug' is not defined.
Because it's just the name of a callable, I can't pass arguments. So if I define the function outside the class (to move into a higher scope and be defined) I can't use the class properties. I have read some suggestions that use lambdas but from the Django documentation that is wrong:
Note that lambdas cannot be used for field options like default
because they cannot be serialized by migrations. See that
documentation for other caveats.
What is the proper way to define a callable for default values in a model.
You shouldn't use this method to set your default value, rather than override the save method of the model and use it there. For example:
class User(models.Model):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
slug = models.SlugField(max_length=256, unique=True, default=uuid.uuid1)
def make_slug(self):
return self.first_name + self.last_name[0]
def save(self, *args, **kwargs):
self.slug = self.make_slug()
super().save(*args, **kwargs)
You get this error
NameError: name 'make_slug' is not defined.
because you refer to make_slug before you defined it. If you moved the make_slug function above the slug field, then you wouldn't get that error.
However, it isn't possible to pass any arguments to the callable that you use as the default, so that won't work either. You can't get around that restriction by using a model method as you are trying.
If you need access to the model instance to calculate the default, then setting the value in the save() method as ruddra suggests is a good idea. Note that you might want to check whether or not the model has a primary key, so that you only create the slug when you first create the instance.
I have a Base class for many Subclasses, and the only thing changing within the subclasses is a certain method (the template pattern). However I am stuck and can't get it to work.
class Base(models.Model):
_value = models.CharField(max_length=200)
_name = models.CharField(max_length=200)
user = models.ForeignKey(User, related_name="some_set")
#used as a property
def value():
def fget(self):
self.refresh()
return self._value
def refresh(self):
raise NotImplementedError("..")
class Subclass1(Base):
def refresh(self):
self._value = some_val
class Subclass2(Base):
def refresh(self):
self._value = some_other_val
I would love to be able to treat the entire related set as the same entity, and call the value property on each, with each deferring to its own implemented version of refresh, i.e.
for x in user.some_set.all():
print x.value
but at this point it doesn't seem possible, even with removing refresh in the superclass. I've also thought of using the Strategy pattern and use a ForeignKey relationship to call the method, but I would still have to have a base class in the ForeignKey that the subclasses derive from.
use Proxy Models
from the doc
Sometimes, however, you only want to change the Python behavior of a model – perhaps to change the default manager, or add a new method.
This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.
I might be missing the point, but have you tried Django Model Utils?
https://bitbucket.org/carljm/django-model-utils/src
If you look at the inheritance manager and make the relevant changes to your model, you should then be able to query as per:
entities = Base.objects.filter(user=my_user_obj).select_subclasses()
for entity in entities:
print entity.value
I ended up using the Strategy pattern with a GenericForeignKey
class Base(models.Model):
_name = models.CharField(max_length=200)
user = models.ForeignKey(User, related_name="some_set")
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
strategy = GenericForeignKey()
#used as a property
def value():
def fget(self):
return self.strategy.value
class Strategy1(models.Model):
#some other definitions
def value():
def fget(self):
return some_var
class Strategy2(models.Model):
#some other definitions
def value():
def fget(self):
return some_other_var
Which allowed me to do for x in user.some_set.all(): print x.value
I'm developing Django application, and I have following error
'Sheep' object has no attribute _state
My models are constructed like this
class Animal(models.Model):
aul = models.ForeignKey(Aul)
weight = models.IntegerField()
quality = models.IntegerField()
age = models.IntegerField()
def __init__(self,aul):
self.aul=aul
self.weight=3
self.quality=10
self.age=0
def __str__(self):
return self.age
class Sheep(Animal):
wool = models.IntegerField()
def __init__(self,aul):
Animal.__init__(self,aul)
What I must do?
firstly, you must be very careful overriding __init__ to have non-optional arguments. remember it will be called every time you get an object from a queryset!
this is the correct code you want:
class Animal(models.Model):
#class Meta: #uncomment this for an abstract class
# abstract = True
aul = models.ForeignKey(Aul)
weight = models.IntegerField(default=3)
quality = models.IntegerField(default=10)
age = models.IntegerField(default=0)
def __unicode__(self):
return self.age
class Sheep(Animal):
wool = models.IntegerField()
I highly suggest setting the abstract option on Animal if you will only ever be using subclasses of this object. This ensures a table is not created for animal and only for Sheep (etc..). if abstract is not set, then an Animal table will be created and the Sheep class will be given it's own table and an automatic 'animal' field which will be a foreign key to the Animal model.
Django docs recommend against you to use __init__ method in models:
You may be tempted to customize the model by overriding the __init__ method. If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved. Rather than overriding __init__, try using one of these approaches:
Add a classmethod on the model class
Add a method on a custom manager (usually preferred)