I'm using Django 3.2 and Python 3.9. In my model, I define an int enum. I would also like to define readable string values for it, so I tried
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0
SELL = 1
labels = {
BUY: 'Buy',
SELL: 'Sell'
}
translation = {v: k for k, v in labels.items()}
but this definition fails with the error
TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'
How would I define strings for each value? I don't mind if the strings are just the literal variable names (e.g. "BUY", "SELL")
Edit: In response to the one of the answers given, seeing this result ...
>>> t = Transaction.objects.all().first()
>>> t.type
0
>>> str(t.type)
'0'
A simpler way to go about it as per django official documentation for Django3.2
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, _('Buy')
SELL = 1, _('Sell')
(or)
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
Another way is to make use of Enum functional api, this is also mentioned in the Django 3.2 official documentation
TransactionTypes = models.IntegerChoices('TransactionTypes', 'BUY SELL')
TransactionTypes.choices
#provides below output
>>>[(1, 'Buy'), (2, 'Sell')]
EDIT: 1
Considering you only have a handful of transaction types (like Buy/Sell and other future transaction type possibilities like Exchange or Return), I would suggest to use PositiveSmallIntegerField which is more apt for your scenario.
Here PositiveSmallIntegerField supports values from 0 to 32767 compared to SmallIntegerField supports values from -32768 to 32767
SYNTAX:
models.PositiveSmallIntegerField(**Field Options)
Example:
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
start_transactionType= models.PositiveSmallIntegerField(choices=TransactionTypes.choices, default=TransactionTypes.BUY, help_text="Do you wish to Buy or Sell?", null=True, blank=True, primary_key=False, editable=True)
def __str__(self):
return '%s' % (self.start_transactionType)
__ str __ is a Python method that returns a string representation of any object. This is what Django uses to display model instances as a plain string.
Field Options
choices : Sets the choices for this field
default: The default value for the field
help_text: Extra “help” text to be displayed with the form widget. It’s useful for documentation even if your field isn’t used on a form
null: If set to True Django stores empty values as NULL in database, by default it is False.
blank: If True, this field is allowed to be blank, by default its False
primary_key: If True, this field is primary key for model, by default it is False
editable: If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True.
For a live example you can follow this 5 part tutorial series,
part 5: Fluent in Django: Get to know Django models better
EDIT: 2
A number of custom properties are added to the enumeration classes – .choices, .labels, .values, and .names – to make it easier to access lists of those separate parts of the enumeration.
As per django documentation use can the .label property or .name property
TransactionTypes.BUY.label
>>> “Buy” #returns this output as string value
TransactionType.BUY.name
>>> “BUY” # returns this output
TransactionType.BUY.value
>>> 0 # returns this as output
EDIT 3 Based on updated question &comments
Brief information covered in Edit 3
extra instance method example quoted from django 3.2 doc
How to apply extra instance method to your use case
Workaround function to solve issues
Django 3.2 documentation on extra instance method mentions
For every field that has choices set, the object will have a get_FOO_display() method, where FOO is the name of the field. This method returns the “human-readable” value of the field.
Sample example from documentation is given below
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>>p = Person(name="Fred Flintstone", shirt_size="L")
>>>p.save()
>>>p.shirt_size
‘L’ #output
>>> p.get_shirt_size_display()
‘Large’ #output
APPLYING extra instance method to your use case
Based on your updated question & comments you have mentioned t to be instance of Transactions object and type to be PositiveSmallIntegerField (an instance of TransactionTypes choices)
The t.get_type_display() code should ideally produce the output Buy as string
>>> type= models.PositiveSmallIntegersField(choices=TransactionTypes.choices, null=True, blank=True)
>>> t = Transaction.objects.all().first()
>>> t.type
0 #output
>>> t.get_type_display()
‘Buy’ #output
Workaround
A workaround is to write a separate function that checks with int enum value and return label as string
def label_of_TransactionType:
if (t.type== TransactionType.BUY.value):
return TransactionTypes.BUY.label
else:
return TransactionTypes.SELL.label
I appreciate the consistent definition, but there are other answer for this question is using enums and I think the Enum type is by far the best. They can show an integer and a string for an item at the same time, while keeping your code more readable.see the code snippets below:
app/enums.py
from enum import Enum
class ChoiceEnum(Enum):
def __str__(self):
return self.name
def __int__(self):
return self.value
#classmethod
def choices(cls):
choices = list()
for item in cls: # Loop thru defined enums
choices.append((item.value, item.name))
return tuple(choices)
class TransactionType(ChoiceEnum):
BUY = 0
SELL = 1
# Uh oh
TransactionType.BUY._name_ = 'Buy'
TransactionType.SELL._name_ = 'Sell'
app/models.py
from django.db import models
from myapp.enums import TransactionType
class Transaction(models.Model):
type_of_transaction = models.IntegerField(choices=TransactionType.choices(), default=int(TransactionType.BUY))
# ...
One way is that you define choices as a Tuple of Tuple, each option being the value of the outer tuple.
The first element in each inner tuple is the value to be set in the model, and the second element being its string representation.like the code snippet below:
class Transaction(models.Model):
BUY = 0
SELL = 1
TRANSACTION_TYPE_CHOICES = (
(BUY, 'Buy'),
(SELL, 'Sell'),
)
type_of_transaction = models.IntegerField(
choices=TRANSACTION_TYPE_CHOICES,
default=BUY,
)
I have a model like this one:
class Extension(models.Model):
name = models.CharField(max_length=32)
version = models.CharField(max_length=16)
#property
def path(self):
return something
I would like to get an Extension queryset containing only the last version for each name and then get a tuple (name, path) for each of these last recent Extension.
I tried
latests = Extension.objects.values('name').annotate(last=Max('version'))
but it gave me a dict without the properties access
How can i do that?
here's one way you could do it:
latests = Extension.objects.values('name').annotate(Max('version'))
q_statement = Q()
for pair in latests:
q_statement |= (Q(name=pair['name']) & Q(version=pair['version__max']))
extensions = Extension.objects.filter(q_statement)
I'm trying to take an object, look up a queryset, find the item in that queryset, and find the next one.
#property
def next_object_url(self):
contacts = Model.objects.filter(owner=self.owner).order_by('-date')
place_in_query = list(contacts.values_list('id', flat=True)).index(self.id)
next_contact = contacts[place_in_query + 1]
When I add this to the model and run it, here's what I get for each variable for one instance.
CURRENT = Current Object
NEXT = Next Object
contacts.count = 1114
self.id = 3533 #This is CURRENT.id
place_in_query = 36
contacts[place_in_query] = NEXT
next_contact = CURRENT
What am i missing / what dumb mistake am i making?
In your function, contacts is a QuerySet. The actual objets are not fetched in the line:
contacts = Model.objects.filter(owner=self.owner).order_by('-date')
because you don’t use a function like list(), you don’t iterate the QuerySet yet... It is evaluated later. This is probably the reason of your problem.
Since you need to search an ID in the list of contacts and the find the next object in that list, I think there is no way but fetch all the contact and use a classic Python loop to find yours objects.
#property
def next_object_url(self):
contacts = list(Model.objects.filter(owner=self.owner).order_by('-date').all())
for curr_contact, next_contact in zip(contacts[:-1], contacts[1:]):
if curr_contact.id == self.id:
return next_contact
else:
# not found
raise ContactNotFoundError(self.id)
Another solution would be to change your database model in order to add a notion of previous/next contact at database level…
Sorry, I couldn't find a suitable title, please edit the title if you understood the problem.
I want to achieve 4 level hierarchy (django Group): Country-Manager,State-Manager, City-Manager and Field-staff.
One user can belong to only one group at a time and any user can add a lead.
I have a model named Lead and I want to realize the following hierarchy:
"User of higher level (say State-Manager) can view leads added by him and and all entries added by users of lower level (say City-Manager and Field Staff) but can not view entries added by other users of same level or higher lever (say Country-Manager)"
To keep track who has added the entry, I am saving user and Group object as foreign key in the Lead model.
Please suggest any strategy or code snippet.
--
p.s: I am on Django 1.5
I solved this problem with help of mixins. ( http://eflorenzano.com/blog/2008/05/17/exploring-mixins-django-model-inheritance/ )
for I create a Hierarchy class:
class Hierarchy(models.Model):
parent = models.ForeignKey('self',null=True,blank=True)
def get_children(self):
return self._default_manager.filter(parent=self)
def get_descendants(self):
descs = set(self.get_children())
for node in list(descs):
descs.update(node.get_descendants())
return descs
class Meta:
abstract = True
and inherited it in class named GroupHirerarchy:
class GroupHierarchy(Hierarchy):
group = models.ForeignKey(Group)
def __unicode__(self):
return self.group.name
Now I can get children of a group using
group_object.get_children()
and all descendants by :
group_object.get_descendants()
Now using this I achieve get hierarchical permission on model:
glist = []
groups = request.user.groups.all()
for group in groups:
try:
group_hierarchy = GroupHierarchy.objects.get(group = group)
group_descendants = group_hierarchy.get_descendants()
for group_descendant in group_descendants:
glist.append(Group.objects.get(name=group_descendant.group))
except:
pass
obj = Model.objects.filter(user_group__in = glist)
I am trying to use this way,
models.py
class Father(models.Model):
name = models.CharField(...)
def last_child_age(self):
children = self.child.order_by('-pk')
if len(children) > 0:
return find_Year(datetime.datetime.now()-children[0].birth_day)
return -1
class Child(models.model):
father = models.ForeignKey(Father, related_name='child')
birth_day = models.DateTimeField(auto_now_add=True)
views.py
def get_old_fathers(request):
father_list = Father.objects.filter(last_child_age__gte=15)
Returns:
Cannot resolve keyword
error.
What is the correct way of making this query other than iterating one by one.
You cant make queries using model class method (passing function to filter) in Django
If you need list of Father objects which have childs with age > 15 years:
d = datetime.date.today()
father_list = Father.objects.filter(
child__birth_day__lte=datetime.date(d.year-15, d.month, d.day))
Without adding flags to Child you can decide which is last (youngest) by making subqueries to child table. Annotation with max birthday (last son -> bigger birth_day date) can help. Try this query (im not test it):
from django.db.models import Max
father_list = Father.objects.annotate(max_birthday=Max('child__birth_day')
).filter(max_birthday__lte=datetime.date(d.year-15, d.month, d.day))
You have not posted your Child model and last_child_age code, but something like this should work:
Child.objects.filter(age__gte=15).prefetch_related('father')
last_child_age is a method, not a column. ORM queries generate SQL, and SQL don't know zilch about your models methods - how would the SQL database call back on your Python code ?