2010-08-12 16 views
13

J'ai un modèle de données avec un champ de bits défini quelque chose comme ceci:Comment devrais-je représenter un champ bit flags int dans l'admin django?

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) 

Et utilisé comme ceci:

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) 

Mes questions sont les suivantes:

A) Quelle est la meilleure façon de représenter les différents bitflags en tant qu'éléments distincts dans le site d'administration? Dois-je remplacer le modèle ou faire quelque chose avec des formulaires?

B) Qu'en est-il de la liste de recherche? Je pourrais créer des fonctions dans le modèle pour représenter chaque bit, mais comment la recherche et le tri seraient-ils faits?

Je suis nouveau à Django.

+1

je me débarrasser des drapeaux de bits en premier lieu. Ils sont mauvais. –

Répondre

3

travail de l'extrait dans la réponse d'Andrew, voici les changements que vous aurez besoin de faire:

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

Vous devez toujours écrire la fonction 'get_prep_lookup'; une façon d'implémenter ceci serait de simplement faire une boucle sur chaque élément de la recherche, et pour chaque élément de boucler 'self.choix »et si le terme de recherche est contenu dans le choix, ajoutez la valeur de ce choix à la clé de recherche. Ainsi, par exemple, votre recherche pourrait être «Access Facility A, Guest spécial» afin qu'elle trouve chaque choix et les additionne, et renvoie «18». –

+0

J'aime ça. Je n'ai pas encore eu le temps de l'essayer. Merci pour les conseils sur la recherche et le filtrage. Que devrais-je faire pour obtenir des colonnes séparées dans la vue de la liste des administrateurs? –

3

Je pense que la meilleure solution ici serait de créer un nouveau type de champ en sous-classes models.Field. Vous pouvez utiliser le paramètre choices pour affecter les indicateurs de bit valides et leurs significations. Cela aidera à garder votre déclaration de modèle propre et facile à lire, avec un résultat final le long des lignes de:

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) 

    ... 

La documentation Django a un article en profondeur sur la façon d'aller sur le sous-classement models.Field:

Writing Custom Model Fields
il semble couvrir tout ce que vous devez faire, y compris: (. Lier un formulaire au champ afin que django-admin sait comment l'afficher)

Si vous cherchez un exemple d'un champ sous-classé, this snippet pourrait être utile. Son but est similaire (plusieurs choix en tant que champ de modèle), mais sa manière de les stocker dans la base de données est différente (il utilise un champ de texte CSV au lieu d'indicateurs de bits).

1

Voici comment j'utiliser les drapeaux avec ma classe User:

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] 

Bien sûr, au lieu d'imprimer les drapeaux, vous pouvez créer une chaîne séparée par des virgules à afficher dans la vue d'administration, ou ce que vous désirez.

Pour l'administrateur, utilisez simplement boolean pour chacune des valeurs de groupe.

3

Une excellente solution testée, même si elle ne correspond pas à votre modèle tout de suite, serait d'utiliser django-bitfield