2012-12-21 60 views
1

我需要一種方法來從另一個ModelAdmin修改一個ModelAdmin成員變量。所以我雖然也許我可以使用全局變量。但是,如果有多個用戶同時使用該應用程序,那麼全局變量會不斷地發生意外變化,並且所有地獄都會崩潰。替代Django中的全局變量?

在Django中有任何方法可以讓我從另一個ModelAdmin修改一個ModelAdmin成員變量嗎?

還是我犯了設計錯誤?我做得比實際還是難,還是我錯過了什麼?使用線程和鎖定怎麼樣?消息傳遞?活動?!?!?!幫助


下面是整個故事。我的應用程序讓用戶通過選擇兼容的CPU,主板,內存和硬盤驅動器(按此順序)來構建PC。通過選擇一個CPU,它們僅限於帶有CPU插座的主板。通過選擇帶有DDR3 DIMM的主板,它們僅限於DDR3內存,等等。我們必須創建ManyToManyField關係,並指定中間表(帶有額外的count字段),並且指定中間表(與其他count字段一起) through arg。這要求使用InlineAdmin模型來顯示管理頁面中的字段。

令我高興的是,raw_id_field變量導致下拉小部件被一個按鈕替換,該按鈕彈出與change_list.html相同的表單,並允許用戶過濾/排序/搜索他們想要的部分。但是,這對我的老闆來說還不夠好。現在我需要根據之前的選擇對這些過濾器進行預定義(即在選擇帶有DDR3的主板後,使用DDR3過濾內存)。所以我加入了這一點:Default filter in Django admin但我需要一種方法來根據他們做出的其他選擇動態地設置CpuAdmin.default_filtersPcAdmin

我的模型,僅包括一個簡潔零件模型:

# models.py 
class CPU(Part): 
    partNum = models.CharField(max_length=60) 
    price = models.DecimalField(precision=2) 
    socket = models.CharField(max_length=60) 
    numCores = models.CharField(max_length=60) 

class PC(models.Model): 
    name = models.CharField(max_length=60) 
    customer = models.CharField(max_length=60) 
    cpuChoices = models.ManyToManyField(CPU, through='PcCpuChoice') 
    memoryChoices = models.ManyToManyField(Memory, through='PcMemoryChoice') 
    hardDriveChoices = models.ManyToManyField(HardDrive, through='PcHardDriveChoice') 
    motherBoardChoices = models.ManyToManyField(MotherBoard, through='PcMotherboardChoice') 

class PcCpuChoice(models.Model): 
    pc = models.ForeignKey(PC, unique=False) 
    cpu = models.ForeignKey(CPU, unique=False) 
    count = models.IntegerField() 

# admin.py 
class PartAdmin(admin.ModelAdmin): 
    class Meta: 
     abstract = True 
    search_fields = ['partNum', 'description', 'model'] 
    default_filter = [] 

    def changelist_view(self, request, extra_context=None): 
     if not request.GET.has_key(self.default_filter[0]): 
      q = request.GET.copy() 
      q[self.default_filter[0]] = self.default_filter[1] 
      request.GET = q 
      request.META['QUERY_STRING'] = request.GET.urlencode() 
     return super(PartAdmin,self).changelist_view(request, extra_context=extra_context) 

class CpuAdmin(PartAdmin): 
    list_filter = ['brand', 'socket', 'numCores', 'graphics'] 
    list_display = ('partNum', 'description', 'brand', 'model', 'markupPrice', 'clockSpeed', 'watts', 'voltage') 
    default_filter = ['numCores','8'] # need to change this from PcAdmin!!! 

class PcCpuInline(admin.TabularInline): 
    model = PcCpuChoice 
    extra = 1 
    max_num = 1 
    raw_id_fields = ['cpu'] 

class PcAdmin(admin.ModelAdmin): 
    inlines = [PcCpuInline, PcMotherboardInline, PcMemoryInline, PcHardDriveInline] 

admin.site.register(PC, PcAdmin) 

回答

0

謝謝@PeterRowell指引我在正確的方向。我用django會話來存儲過濾器,javascript向服務器發送請求,以便在它們加載表單時更新過濾器,並在它們離開表單時刪除它們,處理這些請求的一些視圖函數,添加了一個函數該模型(以請求爲參數)更新已保存的部分的過濾器,並覆蓋PartAdmin的changelist_view函數以使用request.session中的過濾器修改查詢字符串。這是很多不同文件中的很多代碼,但這裏有一些亮點可以幫助尋找這樣的解決方案的人:

發佈在問題中的所有模型都保持幾乎相同。

爲PC的觀點:

def update_filters(request): 
    try: 
     # get POST data 
     url = request.POST['form_url'] 
     id = url.split('/')[-2:-1][0] 
     system_type = url.split('/')[-3:-2][0] 

     if id is not "new": 
      system_content_type = ContentType.objects.get(app_label="systems", model=system_type.rstrip('s')) 
      system = system_content_type.get_object_for_this_type(id=id) 
      system.set_filters(request) 
     else: 
      request.session['filters'] = '' 
     return HttpResponse("Filters where updated.") 
    except: 
     return HttpResponse("Select a part and click 'Save and continue' to set the filters.") 


def delete_filters(request): 
    request.session['filters'] = '' 
    return HttpResponse("Filters where deleted.") 

下面是被放置在change_form.html的JavaScript

function post_to_url(path, params, method) { 
     method = method || "post"; // Set method to post by default, if not specified. 

     // The rest of this code assumes you are not using a library. 
     // It can be made less wordy if you use one. 
     var form = document.createElement("form"); 
     form.setAttribute("method", method); 
     form.setAttribute("action", path); 

     for(var key in params) { 
      if(params.hasOwnProperty(key)) { 
       var hiddenField = document.createElement("input"); 
       hiddenField.setAttribute("type", "hidden"); 
       hiddenField.setAttribute("name", key); 
       hiddenField.setAttribute("value", params[key]); 

       form.appendChild(hiddenField); 
      } 
     } 

     var frame = document.createElement("iframe"); 
     frame.name="hidden-form"; 
     form.target="hidden-form"; 
     document.body.appendChild(form); 
     document.body.appendChild(frame); 
     form.submit(); 
    } 

    // when they load the page, set the filters 
    $(document).ready(function(){ 
     post_to_url("/quotegenerator/systems/update_filters/",{'form_url': document.URL}); 
    }); 

    // when they exit, delete the filter cookie 
    window.onbeforeunload = function() { 
     post_to_url("/quotegenerator/systems/delete_filters/", {}); 
    } 

(通過PcAdmin add_view和change_view功能extra_context中用PARAM添加)最後,添加到PartAdmin的功能:

def set_filters(self, request): 
      # query and get the parts 
      try: 
        cpu = self.cpuChoices.get() 
      except: 
        cpu = False 
      try: 
        mobo = self.motherBoardChoices.get() 
      except: 
        mobo = False 
      try: 
        mem = self.memoryChoices.get() 
      except: 
        mem = False 
      try: 
        hdd = self.hardDriveChoices.get() 
      except: 
        hdd = False 

      # for each combo of parts, figure out whats required 
      # no parts at all 
      if not (mobo or cpu or mem or hdd): 
        request.session['filters'] = '' 
      # mobo only 
      elif mobo and not (cpu or mem or hdd): 
        request.session['filters'] = 'socket='+mobo.socket 
      # cpu only 
      elif cpu and not (mobo or mem or hdd): 
        request.session['filters'] = 'socket='+cpu.socket 
      # memory only 
      elif mem and not (mobo or cpu or hdd): 
        request.session['filters'] = 'memType='+mem.memType 
      # hard drive only 
      elif hdd and not (mobo or cpu or mem): 
        request.session['filters'] = 'interface='+hdd.interface 
      # mobo and cpu 
      elif mobo and cpu and not (mem or hdd): 
        request.session['filters'] = 'memType='+mobo.memType+'&interface='+mobo.interface 
      # mobo and memory 
      elif mobo and mem and not (cpu or hdd): 
        request.session['filters'] = 'socket='+mobo.socket+'&interface='+mobo.interface 
      # mobo and hd 
      elif mobo and hdd and not (cpu or mem): 
        request.session['filters'] = 'socket='+mobo.socket+'&memType='+mobo.memType 
      # cpu and mem 
      elif cpu and mem and not (mobo or hdd): 
        request.session['filters'] = 'socket='+cpu.socket+'&memType='+mem.memType 
      # cpu and hd 
      elif cpu and hdd and not (mobo or mem): 
        request.session['filters'] = 'socket='+cpu.socket+'&interface='+hdd.interface 
      # memory and hd 
      elif mem and hdd and not (mobo or cpu): 
        request.session['filters'] = 'memType='+mem.memType+'&interface='+hdd.interface 
      # mobo cpu and mem 
      elif cpu and mobo and mem and not hdd: 
        request.session['filters'] = 'interface='+mobo.interface 
      # mobo cpu and hd 
      elif mobo and cpu and hdd and not mem: 
        request.session['filters'] = 'memType='+mobo.memType 
      # mobo hd and mem 
      elif mobo and mem and hdd and not cpu: 
        request.session['filters'] = 'socket='+mobo.socket 
      # cpu mem and hd 
      elif cpu and mem and hdd and not mobo: 
        request.session['filters'] = 'socket='+cpu.socket+'&memType='+mem.memType+'&interface='+hdd.interface 
      # all parts 
      else: 
        request.session['filters'] = '' 

哦,和cha PartAdmin的ngelist_view功能確實有點變化:

def changelist_view(self, request, extra_context=None): 
    if ('filters' in request.session): 
     q = request.GET.copy() 
     for filter in request.session['filters'].split('&'): 
      key, value = urllib.splitvalue(filter) 
      # check if the request does not already use the filter 
      # and that the model has the attribute to filter for 
      if (not request.REQUEST.has_key(key)) and (key in self.list_filter): 
       q[key] = value 
     request.GET = q 
     request.META['QUERY_STRING'] = request.GET.urlencode() 
    return super(PartAdmin,self).changelist_view(request, extra_context=extra_context) 
1

這不是一個答案,而是在正確的方向輕推。

還有更多的變量上下文localglobal。在你的情況下,上下文是user或者可能是build(如果用戶有多個同時進行的構建)。

請注意,changelist_view()方法需要一個request對象。從這裏你可以得到usersession(以及任意數量的東西)以及所有其他方式的良好狀態信息。一個進一步的觀察:在一個多線程,多進程的Web環境中,從你習慣的角度來看,確實沒有「全局」。雖然可能在這樣的環境中創建一個「全局」(例如使用memcached),但您將不得不努力工作。

+0

會話呢?餅乾? –

+1

@DanMantyla:是的,我在第三句中提到過。這對於[閱讀文檔](https://docs.djangoproject.com/en/dev/topics/http/sessions/)非常具有啓發性,因爲您可能會發現各種可能對您有用的內容項目。請注意*會話保存時的部分* - 如果您不理解它,並且您在會話中保存了列表或字典,它可能會咬你。 –