2010-08-12 20 views
13

Tôi có một mô hình dữ liệu với một bitfield định nghĩa một cái gì đó như thế này:Làm thế nào tôi nên đại diện cho một bit cờ int lĩnh vực trong 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) 

Và sử dụng như thế này:

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) 

Câu hỏi của tôi là:

A) là cách dễ nhất để đại diện cho gì các bitflags khác nhau dưới dạng các mục riêng biệt trong trang quản trị? Tôi có nên ghi đè lên mẫu hoặc làm điều gì đó với biểu mẫu không?

B) Danh sách tìm kiếm như thế nào? Tôi có thể tạo các hàm trong mô hình để biểu diễn từng bit, nhưng cách tìm kiếm và sắp xếp được thực hiện như thế nào?

Tôi mới làm quen với Django.

+1

Tôi sẽ loại bỏ các cờ bit ở địa điểm đầu tiên. Họ là ác. –

Trả lời

3

Làm việc tắt đoạn trong câu trả lời của Andrew, đây là những thay đổi mà bạn sẽ cần phải thực hiện:

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

Bạn vẫn cần phải viết hàm 'get_prep_lookup'; một cách để thực hiện điều này sẽ là chỉ lặp qua từng mục trong tìm kiếm và cho mỗi mục lặp qua 'tự.lựa chọn' và nếu cụm từ tìm kiếm được chứa trong lựa chọn, sau đó thêm giá trị cho lựa chọn đó vào khóa tra cứu. Vì vậy, ví dụ: tìm kiếm của bạn có thể là "Cơ sở Truy cập A, Khách Đặc biệt", vì vậy, nó sẽ tìm thấy mỗi lựa chọn và thêm chúng, và trả về '18'. –

+0

Tôi thích nó. Chưa có thời gian để thử nó. Cảm ơn các mẹo về tìm kiếm và lọc. Tôi cần phải làm gì để có được các cột riêng biệt trong chế độ xem danh sách quản trị viên? –

3

Tôi nghĩ giải pháp tốt nhất ở đây là để bạn tạo một loại trường mới bằng cách phân lớp models.Field. Bạn có thể sử dụng tham số choices để gán cờ bit hợp lệ và ý nghĩa của chúng. Điều này sẽ giúp giữ cho khai mô hình của bạn sạch sẽ và dễ đọc, với một kết quả cuối cùng dọc theo dòng:

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) 

    ... 

Các tài liệu Django có một tuyệt vời trong bài viết chuyên sâu về cách đi về subclassing models.Field:

Writing Custom Model Fields
dường như để trang trải tất cả mọi thứ bạn cần làm, bao gồm: (. Ràng buộc một hình thức để lĩnh vực này để django-admin biết làm thế nào để hiển thị nó)

Nếu bạn đang tìm kiếm một ví dụ về một lĩnh vực subclassed, this snippet có thể sử dụng. Mục tiêu của nó là tương tự (nhiều lựa chọn như một trường mô hình), nhưng cách lưu trữ chúng trong cơ sở dữ liệu khác nhau (nó sử dụng một trường văn bản CSV thay vì cờ bit).

1

Đây là cách tôi sẽ sử dụng những lá cờ với lớp người dùng của tôi:

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] 

Tất nhiên thay vì in cờ, bạn có thể tạo chuỗi được phân cách bằng dấu phẩy để hiển thị trong chế độ xem quản trị viên hoặc bất kỳ thứ gì bạn muốn.

Đối với quản trị viên, chỉ cần sử dụng boolean cho mỗi giá trị nhóm.

3

Một giải pháp được kiểm tra tuyệt vời, ngay cả khi nó không phù hợp với mô hình của bạn ngay lập tức, sẽ sử dụng django-bitfield

Các vấn đề liên quan