BRANCH_KINDS = ((0, 'Main'), (1, 'Auxiliary'), (2, 'Dead'),)
class Trunk(meta.Model):
name = meta.CharField(max_length=10)
class Branch(meta.Model):
trunk = meta.ForeignKey(Trunk)
kind = meta.IntegerField(choices=BRANCH_KINDS)
Say I have a Trunk object and want to get all of its Auxiliary
branches. How the heck do I do that?
Todd
Since your DB doesn't know anything about BRANCH_KINDS values you should
manually find a number corresponding a value and use it for lookup:
from myproject.myapp.models import BRANCH_KINDS
index = [bk[1] for bk in BRANCH_KINDS].index('Auxillary')
trunks.get_branch_list(kind__exact=BRANCH_KINDS[index][0])
But it anyway looks strange that you need to make a DB lookup based on
values intended only for display purposes and that can be changed any time.
Thanks. It was the second line I couldn't figure out. (Simple in
hindsight.)
Your comment at the end got me thinking, though. Writing
trunk.get_branch(kind__exact=2)
is not very illuminating, but you're correct that the value 'Dead'
could get changed later. In Java, I'd use constants for the integer
values
public static final int DEAD = 2;
but that seems to violate DRY, because the semantics is already
listed in the choices list. I like using integers for what end up
being enumerated types because they don't take much space in the
database and, as you mentioned, it's easy to change the English
version without having to do anything to the db representation.
Is there a better way to do this kind of thing?
Todd
>Your comment at the end got me thinking, though. Writing
>
>trunk.get_branch(kind__exact=2)
>
>is not very illuminating, but you're correct that the value 'Dead'
>could get changed later. In Java, I'd use constants for the integer
>values
>
>public static final int DEAD = 2;
>
>but that seems to violate DRY, because the semantics is already
>listed in the choices list. I like using integers for what end up
>being enumerated types because they don't take much space in the
>database and, as you mentioned, it's easy to change the English
>version without having to do anything to the db representation.
>
>Is there a better way to do this kind of thing?
>
>
This got me thinking too :-)
Generally when I need a constant in Python I don't hesitate to use
string values for constants which are both values and names. So I'd have
BRANCH_KINDS = (('main', 'Main'), ('aux', 'Auxiliary'), ('dead',
'Dead'),)
I think it won't even hurt performance in DB lookups if you create index
for this field. However this implies changing the field to CharField
which won't become a <select> box in admin and in automatic manipulators
(if I'm not mistaken). A lookup table for branch kinds would solve this:
class BranchKind(meta.Model):
id = meta.SlugField(primary_key=True)
title = meta.CharField(maxlength=50)
You can then do something like
trunk.get_branch(pk='dead')
class Constants: """ Construct one of these with keyword arguments, and you can use the attributes. """ def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) def choices(self): return list(self.__dict__.items())(I guess Enumeration would be a better name), then I can define a list of constants:
kBranchKind = Constants( main = 1, aux = 2, dead = 3 )Then in the code, you can use kBranchKind.dead, and in your model, you can use:
class Branch(meta.Model): trunk = meta.ForeignKey(Trunk)
kind = meta.IntegerField(choices=kBranchKind.choices())It keeps the list of choices in one place, gives you run-time errors if you mistype the constant name (string literals would not), and it works just as well with strings for the values.
This got me thinking too :-) Generally when I need a constant in Python I don't hesitate to use string values for constants which are both values and names. So I'd have BRANCH_KINDS = (('main', 'Main'), ('aux', 'Auxiliary'), ('dead', 'Dead'),) I think it won't even hurt performance in DB lookups if you create index for this field. However this implies changing the field to CharField which won't become a <select> box in admin and in automatic manipulators (if I'm not mistaken). A lookup table for branch kinds would solve this: class BranchKind(meta.Model): id = meta.SlugField(primary_key=True) title = meta.CharField(maxlength=50) You can then do something like trunk.get_branch(pk='dead') .
-- Ned Batchelder, http://nedbatchelder.com
This got me thinking too :-) Generally when I need a constant in Python I don't hesitate to use string values for constants which are both values and names. So I'd have BRANCH_KINDS = (('main', 'Main'), ('aux', 'Auxiliary'), ('dead', 'Dead'),) I think it won't even hurt performance in DB lookups if you create index for this field. However this implies changing the field to CharField which won't become a
This got me thinking too :-) Generally when I need a constant in Python I don't hesitate to use string values for constants which are both values and names. So I'd have BRANCH_KINDS = (('main', 'Main'), ('aux', 'Auxiliary'), ('dead', 'Dead'),) I think it won't even hurt performance in DB lookups if you create index for this field. However this implies changing the field to CharField which won't become a
def choices(self): return [(v,k) for k,v in self.__dict__.items()]Then for our example, the choices for Branch.kind would be [(1, 'main'), (2, 'aux'), (3, 'dead')], showing 'main', 'aux', 'dead' in the admin interface. Nicer labels than the Python identifiers wouldn't be possible with this code, you'd have to do something more elaborate:
class K: def __init__(self, label=None, **kwargs):
assert(len(kwargs) == 1)
for k, v in kwargs.items():
self.id = k
self.v = v
self.label = label or self.id
class Constants:
def __init__(self, *args):
self.klist = args
for k in self.klist:
setattr(self, k.id, k.v)
def choices(self):
return [(k.id, k.label) for k in self.klist]
kBranchKind = Constants(
K(main=1, label='Main branch'),
K(dead=2, label='An ex-branch'),
K(aux=3) # I don't know how to spell 'Auxilliary' anyway!
)
class K: def __init__(self, label=None, **kwargs): assert(len(kwargs) == 1) for k, v in kwargs.items(): self.id = k self.v = v self.label = label or self.id class Constants: def __init__(self, *args): self.klist = args for k in self.klist: setattr(self, k.id, k.v) def choices(self): return [(k.id, k.label) for k in self.klist] kBranchKind = Constants( K(main=1, label='Main branch'), K(dead=2, label='An ex-branch'), K(aux=3) # I don't know how to spell 'Auxilliary' anyway! )
def choices(self): return [(k.v, k.label) for k in self.klist]
I can add it for you if you're busy and allow me to... :)
--Ned.
> .
Feel free to fix any mistakes on the page!