Skip to content

Instantly share code, notes, and snippets.

@rg3915
Created March 22, 2023 03:22
Show Gist options
  • Save rg3915/0127010344af2bb38ddc1fde703c3b15 to your computer and use it in GitHub Desktop.
Save rg3915/0127010344af2bb38ddc1fde703c3b15 to your computer and use it in GitHub Desktop.
form add select add form validation Como inserir um novo valor no select do form e contornar o erro de formulário

Como inserir um novo valor no select do form e contornar o erro de formulário?

Eu tenho um formulário com um campo FK, no meu exemplo, Setor.

# models.py
class Produto(models.Model):
    titulo = models.CharField('Título', max_length=255)
    setor = models.ForeignKey(
        Setor,
        on_delete=models.SET_NULL,
        related_name='produtos',
        null=True,
        blank=True
    )

Consequentemente o Setor é um campo do tipo select.

Para inserir novos valores eu estou usando select2, inclusive você pode ver aqui como eu fiz para integrar ele com AlpineJS, vai que um dia você precise.

Bom, com a opção tags: true eu consigo inserir um novo valor no select pelo frontend.

$(".js-example-tags").select2({
  tags: true
});

Mas basicamente ele faz isso aqui

return {
  id: params.term,
  text: params.term
}

ou seja, ele insere o próprio valor da string como id. E isso no backend não faz sentido.

Inicialmente eu estava tentando inserir o valor via Javascript.

$('#id_setor').select2({
  tags: true,  // to add new value
  }).on('select2:close', function(){
    const element = $(this)
    const value = $.trim(element.val())

    if (value != '') {
      fetch('/produto/produtos/setor/create/', {
        method: "POST",
        headers: { "Content-Type": "application/json", "X-CSRFToken": "{{ csrf_token }}" },
        body: JSON.stringify({
          text: value
        }),
      })
        .then(response => response.json())

Pega a visão {{ csrf_token }} de como passar o csrf_token no JS.

Enfim, não deu certo. Ficou muito ruim, prejudicou a usabilidade e deu vários erros.

E o principal motivo desta thread: Form Validation Error

Quando eu liguei o ipdb

form.is_valid() deu False.

E form.errors

{'setor': ['Faça uma escolha válida. Sua escolha não é uma das disponíveis.']}

Dai eu tentei:

  1. Validar os dados no cleaned_data do forms.
def clean(self):
    self.cleaned_data = super().clean()
    setor, _ = Setor.objects.get_or_create(nome=self.data.get('setor'))
    self.cleaned_data['setor'] = data['setor']
    return self.cleaned_data

Mas não deu certo, o formulário continuava dando erro.

  1. Tentei usar um initial no form.

Doc do initial

Dai eu tentei

# views.py
def produto_create(request):
    template_name = 'produto/produto_form.html'
    form = ProdutoForm()

    if request.method == 'POST':
        form = ProdutoForm(data, initial={'setor': setor})

        # obj = form.save(commit=False)  # TENTEI TB, E NADA.

        # del form.errors['setor']  # OLHA A GAMBIARRA MONSTRO QUE EU TENTEI KKKKKKKKK

        if form.is_valid():
            form.save()

        return redirect('produto:produto_list')

# forms.py
class ProdutoForm(forms.ModelForm):

    class Meta:
        model = Produto
        fields = (
            'titulo',
            'setor',
        )

    def clean(self):
        self.cleaned_data = super().clean()
        data = self.initial
        setor, _ = Setor.objects.get_or_create(nome=data)
        self.cleaned_data['setor'] = data['setor']
        return self.cleaned_data

Nada.

  1. Finalmente consegui resolver com copy()
def produto_create(request):
    template_name = 'produto/produto_form.html'
    form = ProdutoForm()

    if request.method == 'POST':
        setor_nome = request.POST.get('setor')
        setor, _ = Setor.objects.get_or_create(nome=setor_nome)

        data = request.POST.copy()  # sem o copy ele diz que é imutável.
        data['setor'] = setor

        form = ProdutoForm(data)

        if form.is_valid():
            form.save()

        return redirect('produto:produto_list')

    context = {'form': form}
    return render(request, template_name, context)

E deletei o clean(). E funcionou!!!

Mas e ai? Essa é a melhor solução?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment