2010-08-12 4 views
13

Tengo un modelo de datos con un campo de bits se define así:¿Cómo debería representar un campo int de bit flags en django admin?

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) 

y usados ​​como esto:

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) 

Mis preguntas son:

A) ¿Cuál es la forma más fácil de representar las diferentes bitflags como elementos separados en el sitio de administración? ¿Debo anular la plantilla o hacer algo con los formularios?

B) ¿Qué hay de la lista de búsqueda? Pude crear funciones en el modelo para representar cada bit, pero ¿cómo se haría la búsqueda y la clasificación?

Soy nuevo en Django.

+1

me deshago de los indicadores de bits en el primer lugar. Ellos son malvados –

Respuesta

3

Trabajar fuera el fragmento en la respuesta de Andrew, aquí están los cambios que había necesidad de hacer:

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

Aún necesitaría escribir la función 'get_prep_lookup'; Una forma de implementar esto sería simplemente recorrer cada elemento en la búsqueda, y para cada elemento, pasar por 'self.opciones' y si el término de búsqueda está contenido en la opción, entonces agregue el valor de esa opción a la clave de búsqueda. Por lo tanto, por ejemplo, su búsqueda podría ser "Acceso a la Instalación A, Invitado Especial" para que encuentre cada opción y las sume, y devuelva '18'. –

+0

Me gusta. Aunque no he tenido tiempo de probarlo. Gracias por los consejos sobre búsqueda y filtrado. ¿Qué debería hacer para obtener columnas separadas en la vista de la lista de administración? –

3

Creo que la mejor solución aquí sería crear un nuevo tipo de campo mediante la subclasificación models.Field. Puede usar el parámetro choices para asignar los indicadores de bits válidos y sus significados. Esto ayudaría a mantener su modelo de declaración limpio y fácil de leer, con un resultado final a lo largo de las líneas 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 documentación de Django tiene un gran artículo en profundidad sobre cómo ir sobre la subclasificación models.Field:

Writing Custom Model Fields
parece cubrir todo lo que tiene que hacer, incluyendo: (. Atar un formulario para el campo para que django-admin sabe cómo mostrarlo)

Si lo que buscas es un ejemplo de un campo de subclase, this snippet podría ser de utilidad. Su objetivo es similar (opciones múltiples como campo de modelo), pero su forma de almacenarlos en la base de datos difiere (está utilizando un campo de texto CSV en lugar de indicadores de bits).

1

Así es como me gustaría utilizar las banderas con mi clase Usuario:

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] 

Por supuesto, en lugar de imprimir las banderas, puede crear una cadena separada por comas para mostrar en la vista de administrador o lo que desee.

Para el administrador, solo use un boolean para cada uno de los valores del grupo.

3

Una gran solución probada, incluso si no se ajusta a su modelo de inmediato, habría utilizando django-bitfield

Cuestiones relacionadas