2010-08-12 4 views
13

Ich habe ein Datenmodell mit einem Bitfeld definiert etwas wie folgt aus:Wie sollte ich ein Bit Flags-Feld in Django Admin darstellen?

alter table MemberFlags add column title varchar(50) not null default ''; 
alter table MemberFlags add column value integer(3) not null default 0; 

insert into MemberFlags (title, value) values 
    ("Blacklisted",    1), 
    ("Special Guest",   2), 
    ("Attend Ad-hoc Sessions", 4), 
    ("Attend VIP Sessions",  8), 
    ("Access Facility A",  16), 
    ("Access Facility B",  32) 

Und so benutzten:

alter table Membership add column title varchar(50) not null default ''; 
alter table Membership add column flags integer(3) not null default 0; 

insert into Membership (title, flags) values 
    ("Guest Pass",   4+2), 
    ("Silver Plan", 16+ 4 ), 
    ("Gold Plan", 32+16+ 4+2), 
    ("VIP Pass", 32+16+8+4+2) 

Meine Fragen sind:

A) Was ist der einfachste Weg ist, zu repräsentieren die verschiedenen Bitflags als separate Elemente in der Admin-Site? Sollte ich die Vorlage überschreiben oder etwas mit Formularen machen?

B) Wie wäre es mit der Suchliste? Ich könnte Funktionen im Modell erstellen, um jedes Bit darzustellen, aber wie würde Suchen und Sortieren durchgeführt werden?

Ich bin neu in Django.

+1

Verwendung sein, die ich des Bitflags in erster Linie loswerden würde. Sie sind böse. –

Antwort

3

Snippet in Andrew Antwort Arbeiten aus, hier sind die Änderungen, die Sie benötigen würden zu machen:

from django.db import models 
from django import forms 

class BitFlagFormField(forms.MultipleChoiceField): 
    widget = forms.CheckboxSelectMultiple 

    def __init__(self, *args, **kwargs): 
     super(BitFlagFormField, self).__init__(*args, **kwargs) 

class BitFlagField(models.Field): 
    __metaclass__ = models.SubfieldBase 

    def get_internal_type(self): 
     return "Integer" 

    def get_choices_default(self): 
     return self.get_choices(include_blank=False) 

    def _get_FIELD_display(self, field): 
     value = getattr(self, field.attname) 
     choicedict = dict(field.choices) 

    def formfield(self, **kwargs): 
     # do not call super, as that overrides default widget if it has choices 
     defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 
        'help_text': self.help_text, 'choices':self.choices} 
     if self.has_default(): 
      defaults['initial'] = self.get_default() 
     defaults.update(kwargs) 
     return BitFlagFormField(**defaults) 

    def get_db_prep_value(self, value): 
     if isinstance(value, int): 
      return value 
     elif isinstance(value, list): 
      return sum(value) 

    def to_python(self, value): 
     result = [] 
     n = 1 
     while value > 0: 
      if (value % 2) > 0: 
       result.append(n) 
      n *= 2 
      value /= 2 
     return sorted(result) 


    def contribute_to_class(self, cls, name): 
     super(BitFlagField, self).contribute_to_class(cls, name) 
     if self.choices: 
      func = lambda self, fieldname = name, choicedict = dict(self.choices):" and ".join([choicedict.get(value,value) for value in getattr(self,fieldname)]) 
      setattr(cls, 'get_%s_display' % self.name, func) 
+0

Sie müssten immer noch die 'get_prep_lookup' Funktion schreiben; Eine Möglichkeit, dies zu implementieren, wäre, einfach jedes Element in der Suche durchzuschleifen und für jedes Element selbst durchzulaufen.Auswahlmöglichkeiten "und wenn der Suchbegriff in der Auswahl enthalten ist, fügen Sie den Wert für diese Auswahl dem Suchschlüssel hinzu. Zum Beispiel könnte Ihre Suche "Access Facility A, Special Guest" sein, so dass es jede Auswahl finden und addieren und "18" zurückgeben würde. –

+0

Ich mag es. Ich hatte noch keine Zeit es zu versuchen. Danke für die Tipps zum Suchen und Filtern. Was müsste ich tun, um separate Spalten in der Admin-Listenansicht zu erhalten? –

3

Ich denke, die beste Lösung hier wäre für Sie, einen neuen Feldtyp durch Unterklassen models.Field zu erstellen. Sie können den Parameter choices verwenden, um die gültigen Bit-Flags und ihre Bedeutungen zuzuweisen. Dies würde helfen, Ihre Modell-Deklaration sauber und gut lesbar, mit einem Endergebnis entlang der Linien von:

class BitFlagField(models.Field): 

    ... 

class MyModel(models.Model): 

    ... 

    FLAG_CHOICES = (
     (1, 'Blacklisted'), 
     (2, 'Special Guest'), 
     (4, 'Attend Ad-hoc Sessions'), 
     (8, 'Attend VIP Sessions'), 
     (16, 'Access Facility A'), 
     (32, 'Access Facility B'), 
    ) 
    flags = BitFlagField(choices=FLAG_CHOICES) 

    ... 

Die Django-Dokumentation hat eine große in ausführlichen Artikel darüber, wie über Subklassen models.Field gehen:

Writing Custom Model Fields
es scheint, einschließlich tun alles, was Sie decken müssen: (., ein Formular zu dem Feld binden, so dass django-admin weiß, wie es angezeigt)

Wenn Sie suchen ein Beispiel für eine Unterklasse-Feld könnte this snippet von Nutzen sein. Das Ziel ist ähnlich (mehrere Auswahlmöglichkeiten als Modellfeld), aber seine Art, sie in der Datenbank zu speichern, ist unterschiedlich (es wird ein CSV-Textfeld anstelle von Bit-Flags verwendet).

1

Dies ist, wie ich die Flaggen mit meiner User-Klasse verwenden würde:

FLAGS = { 
    1:"Blacklisted", 
    2:"SpecialGuest", 
    4:"AttendAd-hocSessions", 
    8:"AttendVIPSessions", 
    16:"AccessFacilityA", 
    32:"AccessFacilityB", 
} 

class User(object): 
    def __init__(self, name="John Doe", groups=0): 
     self.name = name 
     self.groups = groups 
    def memberof(self): 
     ''' Display string representation of the groups. ''' 
     for flag in sorted(FLAGS): 
      if (flag & self.groups) == flag: 
       print FLAGS[flag] 

Natürlich können Sie, anstatt die Flags zu drucken, eine durch Kommas getrennte Zeichenfolge erstellen, um sie in der Admin-Ansicht anzuzeigen, oder was immer Sie wünschen.

Für den Admin, benutzen Sie einfach eine boolean für jede der Gruppenwerte.