【问题标题】:Alternative to global variables in Django?Django中全局变量的替代品?
【发布时间】:2012-12-09 04:52:34
【问题描述】:

我需要一种方法来从另一个 ModelAdmin 修改一个 ModelAdmin 成员变量。所以我虽然也许我可以使用一个全局变量。但是,如果有多个用户同时使用该应用程序,那么全局变量将继续意外更改,一切都会崩溃。

Django 中是否有任何方法可以让我从另一个 ModelAdmin 修改一个 ModelAdmin 成员变量?

还是我犯了设计错误?我是不是让这件事变得比实际更难,还是我错过了什么?那么使用线程和锁呢?消息传递???活动?!?!?!帮助


这就是整个故事。我的应用程序允许用户通过选择兼容的 CPU、主板、内存和硬盘驱动器(按此顺序)来构建 PC。通过选择 CPU,它们仅限于带有 CPU 插槽的主板。通过选择带有 DDR3 调光器的主板,它们会受到 DDR3 内存的限制,等等。

另外,感觉每个系统可能有许多相同的部分(例如:内存模块,但它们必须相同),我必须创建 ManyToManyField 关系并指定中间表(带有额外的 count 字段) through 参数。这需要使用 InlineAdmin 模型来显示管理页面中的字段。

令我高兴的是,raw_id_field 变量导致下拉小部件被替换为一个按钮,该按钮弹出一个与 change_list.html 相同的表单,并允许用户过滤/排序/搜索他们想要的部分。然而,这对我的老板来说还不够好。现在我需要根据之前的选择来预定义这些过滤器(即在选择带有 DDR3 的主板后过滤带有 DDR3 的内存)。所以我暗示了这个:Default filter in Django admin,但我需要一种方法来根据他们做出的其他选择动态地从PcAdmin 设置CpuAdmin.default_filters

我的模型,为简洁起见只包括一个零件模型:

# 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)

【问题讨论】:

    标签: python django django-admin global-variables


    【解决方案1】:

    感谢@PeterRowell 为我指明了正确的方向。我使用 django session 来存储过滤器,使用 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(通过 PcAdmin add_view 和 change_view 函数中的 extra_context 参数添加)

        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/", {});
        }
    

    最后,添加到 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'] = ''
    

    哦,PartAdmin 的 changelist_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)
    

    【讨论】:

      【解决方案2】:

      这不是一个答案,而是朝着正确的方向轻推。

      除了localglobal,还有更多变量上下文。在您的情况下,上下文是 userbuild(如果用户同时进行多个构建)。

      请注意,changelist_view() 方法采用 request 对象。从中您可以获得usersession(上面挂着任意数量的东西)以及所有其他形式的良好状态信息。

      进一步观察:在多线程、多进程的 Web 环境中,确实没有您习惯认为的“全局”。虽然可能在这样的环境中创建一个“全局”(例如使用memcached),但您将不得不非常努力地工作。

      【讨论】:

      • 会话怎么样?饼干?
      • @DanMantyla:是的,我在第三句话中提到了这一点。 read the docs 在这方面非常具有指导意义,因为您可能会发现各种可能对您的项目有用的东西。请注意关于保存会话时的部分——如果你不理解它并且你在会话中保存列表或字典,它可能会咬你。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-06
      • 1970-01-01
      • 2021-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-01
      相关资源
      最近更新 更多