2012-03-16 15 views
6

Ho problemi a limitare le scelte selezionabili in un formset. Ho i seguenti modelli: impiegati, dipartimento, progetto, tipo di progetto, appartenenza e ruolo. Un dipendente può aggiungere/rimuovere i ruoli che interpreta per un determinato progetto di reparti nel formset, il modulo dovrebbe limitare i progetti selezionabili solo a quelli appartenenti al reparto a cui appartiene il dipendente.django: come limitare le scelte di campo in formset?

MODELLI:

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

In questo momento una UE può scegliere un ruolo da svolgere per ogni progetto reparti. Si sta comportando come questo:

Opzioni progetto:

Projects.objects.all() 

voglio limitare i progetti con qualcosa di simile: chocies LIMITE progetto per:

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

[Questa domanda Stack Overflow] (http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset) è abbastanza simile. Ci sono due approcci che funzionano. 1) creare un modulo che prende dipendente come argomento nel suo metodo '__init__' e usa la funzione curry. 2) Costruisci la classe del modulo nella funzione di visualizzazione.Se non è necessario riutilizzare il modulo altrove, trovo più semplice il secondo approccio. – Alasdair

+0

new to python, strutturate la classe form nella vista proprio come fareste nel model.py? – thedeepfield

+0

Ho ampliato il mio commento come risposta sotto. – Alasdair

risposta

7

Questo Stack Overflow question è abbastanza simile. Mi piace l'approccio della risposta di Matthew, in cui costruisci il modulo dinamicamente in una funzione che ha accesso al dipendente tramite la chiusura. Nel tuo caso, si desidera qualcosa di simile:

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

supponiamo che la classe MembershipForm sia nella vista o nel modello? – thedeepfield

+0

Viene visualizzato un errore che dice 'return' all'esterno della funzione – thedeepfield

+0

I form in realtà non appartengono a 'models.py'. Nelle app Django più grandi, a volte hai un modulo 'forms.py'. In questo caso, puoi mettere la funzione 'make_membership_form' nel' views.py' se vuoi. – Alasdair

3

EDIT

Darn . Tutto ciò che digitando perché ho perso una parte del codice;). Come @Alasdair menziona nei commenti, hai escluso department dal modulo, quindi puoi limitarlo con Django. Ho intenzione di lasciare la mia risposta originale, però, nel caso in cui potrebbe aiutare qualcun altro.

per le circostanze, tutto ciò che serve è:

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) 

E, poi:

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

Risposta originale (per i posteri)

Non è possibile limitare questo Django, perché il valore per reparto è variabile, e quindi l'elenco dei progetti può variare a seconda di quale reparto specifico è selezionato a mome nt. Per convalidare il modulo, dovrai alimentare tutti i possibili progetti che potrebbero essere consentiti a Django, quindi l'unica opzione è AJAX.

Creare una vista che restituisca una risposta JSON costituita da progetti per un particolare reparto alimentati nella vista. Qualcosa sulla falsariga di:

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

Poi, creare un po 'di JavaScript per andare a prendere questo punto di vista ogni volta che la casella di selezione reparto è cambiata:

(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

'dipartimento' non è modificabile - nota' exclude = ('reparto', 'impiegato'),) '. – Alasdair

+0

OMG GRAZIE, sono rimasto bloccato su questo argomento per una settimana. Ho visto una risposta simile buttata in giro ma non ho mai potuto capire appieno cosa sta succedendo. puoi per favore dirmi cosa significa questo mago codificatore? Riferimenti? – thedeepfield

+0

Inoltre, myset ha un modulo vuoto aggiuntivo per la creazione di una nuova voce di appartenenza. Tuttavia, il campo del progetto in questa nuova forma non contiene scelte ... perché le mie scelte mancano ora? – thedeepfield

Problemi correlati