2012-03-16 15 views
6

Tengo problemas para limitar las opciones seleccionables en un formset. Tengo los siguientes modelos: Empleados, Departamento, Proyecto, Tipo de proyecto, Membresía y Rol. Un empleado puede agregar/eliminar las funciones que desempeñan para un proyecto de departamentos determinado en el formset, el formulario debe limitar los proyectos seleccionables a solo aquellos que pertenecen al departamento al que pertenece el empleado.django: ¿Cómo limitar las opciones de campo en formset?

MODELOS:

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",),) 

Vista:

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

En este momento una UE puede seleccionar una función que cumplir para cualquier proyecto de departamentos. Se está actuando de esta manera:

Opciones del proyecto:

Projects.objects.all() 

quiero limitar los proyectos con algo como esto: chocies PROYECTO límite a:

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

[Esta pregunta desbordamiento de pila] (http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset) es bastante similar. Hay dos enfoques que funcionan. 1) cree un formulario que tome como argumento al empleado en su método '__init__' y use la función curry. 2) Construye la clase de formulario en la función de vista.Si no necesita reutilizar el formulario en otro lugar, me resulta más fácil el segundo enfoque. – Alasdair

+0

nuevo para Python, ¿estructura la clase de formulario en la vista como lo haría en el modelo.py? – thedeepfield

+0

He ampliado mi comentario como respuesta a continuación. – Alasdair

Respuesta

7

Este Stack Overflow question es bastante similar. Me gusta el enfoque de la respuesta de Matthew, donde construyes el formulario dinámicamente en una función que tiene acceso al empleado a través del cierre. En su caso, usted quiere algo como:

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)) 
+0

¿La clase MembershipForm supone estar en la vista o en el modelo? – thedeepfield

+0

Aparece un error que dice 'return' fuera de la función – thedeepfield

+0

Las formas no pertenecen realmente a 'models.py'. En las aplicaciones de Django más grandes, a veces tienes un módulo 'forms.py'. En este caso, puede poner la función 'make_membership_form' en' views.py' si lo desea. – Alasdair

3

EDIT

Darn . Todo ese tipeo porque me perdí una parte del código;). Como @Alasdair menciona en los comentarios, ha excluido department del formulario, por lo que puede limitar esto con Django. Sin embargo, voy a dejar mi respuesta original, en caso de que pueda ayudar a alguien más.

para sus circunstancias, todo lo que necesita es:

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) 

Y, a continuación:

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

respuesta original (para la posteridad)

no se puede limitar esto en Django, porque el valor para el departamento es variable y, por lo tanto, la lista de proyectos puede variar según el departamento particular que se seleccione en el módem Nuevo Testamento. Para validar el formulario, deberá alimentar a Django todos los proyectos posibles que puedan permitirse, por lo que su única opción es AJAX.

Crea una vista que devolverá una respuesta JSON que consiste en proyectos para un departamento particular alimentado a la vista. Algo a lo largo de las líneas de:

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

A continuación, crear un poco de JavaScript a buscar este punto de vista cada vez que se cambia el cuadro de selección departamento:

(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); 
+0

'departamento' no es modificable - nota' exclude = ('departamento', 'empleado'),) '. – Alasdair

+0

OMG GRACIAS, he estado atascado en este tema durante una semana. He visto una respuesta similar, pero nunca pude entender completamente qué está pasando. ¿Puedes decirme qué significa este asistente de codificación? referencias? – thedeepfield

+0

Además, my fromset tiene un formulario en blanco adicional para crear una nueva entrada de membresía. Sin embargo, el campo del proyecto en esta nueva forma no contiene opciones ... ¿por qué mis opciones faltan ahora? – thedeepfield

Cuestiones relacionadas