django: Как ограничить выбор полей в formset? - PullRequest
6 голосов
/ 16 марта 2012

У меня проблемы с ограничением выбора в наборе форм. У меня есть следующие модели: сотрудники, отдел, проект, тип проекта, членство и роль. Сотрудник может добавлять / удалять роли, которые они играют для данного проекта отделов в наборе форм, форма должна ограничивать выбираемые проекты только теми, которые принадлежат отделу, к которому принадлежит сотрудник.

МОДЕЛЬ:

class Department(models.Model):
    name = models.CharField(max_length=20)
    def __unicode__(self):
    return self.name

class Employee(models.Model):
    fname = models.CharField(max_length=15)
    department = models.ForeignKey(Department)
    def __unicode__(self):
        return self.fname

class Projecttype(models.Model):
    name = models.CharField(max_length=20)
    def __unicode__(self):
        return self.name

class Project(models.Model):
    projecttype = models.ForeignKey(Projecttype)
    department = models.ForeignKey(Department)
    members = models.ManyToManyField(Employee, through='Membership')
    def __unicode__(self):
       return "%s > %s" % (self.department, self.projecttype)

class Role(models.Model):
    name = models.CharField(max_length=20)
    def __unicode__(self):
       return self.name

class Membership(models.Model):
    project = models.ForeignKey(Project, null=True)
    department = models.ForeignKey(Department)
    employee = models.ForeignKey(Employee)
    role = models.ManyToManyField(Role, blank=True, null=True)
    class Meta:
        unique_together = (("project", "employee",),)

ВИД:

def employee_edit(request, employee_id):
    i = get_object_or_404(Employee, pk=employee_id)
    MembershipFormSet = modelformset_factory(Membership, exclude=('department', 'employee'),)
    f = MembershipFormSet(queryset=Membership.objects.filter(employee=i),)
    return render_to_response('gcs/edit.html', {'item': i, 'formset': f, }, context_instance=RequestContext(request))

В настоящее время ЕС может выбрать роль для любого проекта департамента. Это действует так:

Параметры проекта:

Projects.objects.all()

Я хочу ограничить проекты чем-то вроде этого: ОГРАНИЧИТЬ ВЫБОР ПРОЕКТА ДО:

Projects.objects.filter(department=i.department)

Ответы [ 2 ]

7 голосов
/ 17 марта 2012

Этот вопрос переполнения стека довольно похож.Мне нравится подход ответа Мэтью, где вы динамически строите форму в функции, которая имеет доступ к сотруднику через закрытие.В вашем случае вы хотите что-то вроде:

from django.http import HttpResponseRedirect

def make_membership_form(employee):
    """
    Returns a Membership form for the given employee, 
    restricting the Project choices to those in the 
    employee's department. 
    """
    class MembershipForm(forms.ModelForm):
        project = forms.ModelChoiceField(queryset=Projects.objects.filter(department=employee.department))
        class Meta:
            model = Membership
            excludes = ('department', 'employee',)
    return MembershipForm

def employee_edit(request, employee_id):
    employee = get_object_or_404(Employee, pk=employee_id)
    # generate a membership form for the given employee
    MembershipForm = make_membership_form(employee)
    MembershipFormSet = modelformset_factory(Membership, form=MembershipForm)

    if request.method == "POST":
        formset = MembershipFormSet(request.POST, queryset=Membership.objects.filter(employee=employee))
        if formset.is_valid():
            instances = formset.save(commit=False)
                for member in instances:
                    member.employee = employee
                    member.department = employee.department
                    member.save()
            formset.save_m2m()
            # redirect after successful update
            return HttpResponseRedirect("")
    else:
        formset = MembershipFormSet(queryset=Membership.objects.filter(employee=employee),)
    return render_to_response('testdb/edit.html', {'item': employee, 'formset': formset, }, context_instance=RequestContext(request))
4 голосов
/ 16 марта 2012

РЕДАКТИРОВАТЬ

Черт.Все это печаталось, потому что я пропустил одну часть кода;).Как упоминает @Alasdair в комментариях, вы исключили department из формы, поэтому вы можете ограничить это с помощью Django.Я собираюсь оставить свой оригинальный ответ, однако, на всякий случай, если он может помочь кому-то еще.

Для ваших обстоятельств все, что вам нужно, это:

class MembershipForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MembershipForm, self).__init__(*args, **kwargs)
        self.fields['project'].queryset = self.fields['project'].queryset.filter(department_id=self.instance.department_id)

И затем:

MembershipFormSet = modelformset_factory(Membership, form=MembershipForm, exclude=('department', 'employee'),)

Оригинальный ответ (для потомков)

Вы не можете ограничить это в Django, потому что значение для отдела изменчиво, и, таким образом, список проектов можетварьироваться в зависимости от того, какой именно отдел выбран в данный момент.Чтобы проверить форму, вам нужно будет передать все возможные проекты, которые могут быть разрешены для Django, поэтому единственный вариант - AJAX.

Создайте представление, которое будет возвращать ответ JSON, состоящий из проектов дляконкретный отдел подпитывается в поле зрения.Что-то вроде:

from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_list_or_404
from django.utils import simplejson

def ajax_department_projects(request):
    department_id = request.GET.get('department_id')
    if department_id is None:
        return HttpResponseBadRequest()

    project_qs = Project.objects.select_related('department', 'project_type')
    projects = get_list_or_404(project_qs, department__id=department_id)
    data = []
    for p in projects:
        data.append({
            'id': p.id,
            'name': unicode(p),
        })

    return HttpResponse(simplejson.dumps(data), mimetype='application/json')

Затем создайте немного JavaScript, чтобы извлекать это представление при каждом изменении поля выбора отдела:

(function($){
    $(document).ready(function(){
        var $department = $('#id_department');
        var $project = $('#id_project');

        function updateProjectChoices(){
            var selected = $department.val();
            if (selected) {
                $.getJSON('/path/to/ajax/view/', {department_id: selected}, function(data, jqXHR){
                    var options = [];
                    for (var i=0; i<data.length; i++) {
                        output = '<option value="'+data[i].id+'"';
                        if ($project.val() == data[i].id) {
                            output += ' selected="selected"';
                        }
                        output += '>'+data[i].name+'</option>';
                        options.push(output);
                    }
                    $project.html(options.join(''));
                });
            }
        }

        updateProjectChoices();
        $project.change(updateProjectChoices);
    });
})(django.jQuery);
...