views:

113

answers:

3

I have a model Menu:

class Menu(models.Model):
    loja = models.ForeignKey(Loja, related_name='menus')
    nome = models.CharField(max_length=30)
    ordenacao = models.IntegerField(blank=True, null=True)

    class Meta:
        ordering = ('ordenacao',)
        #prevent equally named menus within a store(loja)
        unique_together = ('loja', 'nome')

    def __unicode__(self):
        return self.nome

The Menu form:

class MenuForm(ModelForm):
    class Meta:
        model = Menu
        exclude =('loja', 'ordenacao',)

Add menu view:

def addmenu(request, s_loja):
    loja = get_object_or_404(Loja, slug=s_loja)
    if not loja.activo:
        return render_to_response('painelcontrolo/loja_detail.html', {
            'notificacoes': ['Loja está definida como inactivo.', \
                             'Alterações não permitidas']})
    if request.method == 'POST':
        menu = Menu(loja=loja)
        form = MenuForm(request.POST, instance=menu)
        if form.is_valid():
            menu_existe = Menu.objects.\
                          filter(nome=form.cleaned_data['nome']).\
                          filter(loja=loja)
            if menu_existe:
                return render_to_response(\
                    'painelcontrolo/loja_detail.html', {
                        'notificacoes': ['Já existe um menu com esse nome']
                        })
            form.save()
            return render_to_response(\
                    'painelcontrolo/loja_detail.html', {
                        'notificacoes': ['Menu adicionado']
                        })
    else:
        form = MenuForm()
    return render_to_response('form_as_p.html', {
        'form': form
    })

The error:(no longer occurs with the added validation on the addmenu view)

Request Method:     POST
Request URL:    http://127.0.0.1:8000/painel/bispos/addmenu/
Exception Type:     IntegrityError
Exception Value:    columns loja_id, nome are not unique

The question: The form is valid, but the model isn't if nome+loja_id already exist on the db. Do I need to add this validation somewhere else? And where?

edit: I wrote a validation on the view and it passes a notification to the template and this is fine, but not perfect. I would like to re-display the form with the user input to give an opportunity to fix what's wrong without losing that information. Is there a way to do that?

A: 

I'm not really sure how the form would know about what unique values exist in the database, it just validates the type of fields required to populate the model, as far as I know.

In other words "form.is_valid()" is not going to save to the database, which is the only way you can see if the save works.

You will have to catch this on your own, I'm pretty sure, but "unique_together" is already doing it's job.

Dan Ostrowski
A: 

I would just leave the loja in the form but make it a hidden field.

class MenuForm(ModelForm):
    loja = models.ModelChoiceField(Loja.objects.all(), widget=forms.HiddenInput)

    class Meta:
        model = Menu
        exclude =('ordenacao',)

You probably would have to change your view to call your getloja() regardless if the request is a post or get. You never explain HOW getloja() decides what is the proper instance...

@login_required
def addmenu(request, s_loja):
    if request.method == 'POST':
        form = MenuForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/painel/profile/')
    else:
        loja = getloja(request, s_loja) #simply retrieves the correct loja instance
        menu = Menu(loja=loja)
        form = MenuForm(instance=menu)
    return render_to_response('form_as_p.html', {
           'form': form,})
celopes
But then I the form can't be submitted, because the field can't be empty. It seems like a better solution, but I'm not sure how to make it work. I tried with loja = forms.ModelChoiceField(label="", queryset=Loja.objects.all(), widget=forms.HiddenInput())
Ricardo B.
Ricardo, what is getloja(request, s_loja) doing? Could you post it?
celopes
Oh, I just realized... `s_loja` is the url parameter passed in to the view, correct? getloja is probably just doing a `Loja.objects.get(...)` (I just don't know why you are passing the request into it). Just move getloja() to right before the form=MenuForm(). I'll edit my answer...
celopes
Look at the view code in my answer now... I still don't know why `getloja()` needs the request though...
celopes
it doesn't. I'll update the code above.
Ricardo B.
the hidden field idea sounded good, but doesn't seem to work. The only way it would work is if I don't validate the form.
Ricardo B.
A: 

Ok, this is the best I could come up with. It gives the error on the form and seems to work fine.

@login_required
def addmenu(request, s_loja):
    loja = get_object_or_404(Loja, slug=s_loja)
    if not loja.activo:
        return render_to_response('painelcontrolo/loja_detail.html', {
            'notificacoes': ['Loja está definida como inactivo.', \
                             'Alterações não permitidas']})
    if request.method == 'POST':
        menu = Menu(loja=loja)
        form = MenuForm(request.POST, instance=menu)
        if form.is_valid():
            menu_existe = Menu.objects.\
                          filter(nome=form.cleaned_data['nome']).\
                          filter(loja=loja)
            if not menu_existe:
                form.save()
                return render_to_response('painelcontrolo/loja_detail.html', {
                        'notificacoes': ['Menu adicionado']
                        })
            else:
                form._errors['nome'] = ErrorList(\
                    ['Um menu com esse nome já existe'])
    else:
        form = MenuForm()
    return render_to_response('form_as_p.html', {
        'form': form,
    })
Ricardo B.