0

I'd like the view only show the "personajes" that were created by the current user.

If I I'm not understanding wrong, what I have to do is filter the choices, depending who is the current user logged in, before the form render. I guess that what I have to edit is the queryset argument in ModelChoiceField(**kwargs) but I don't know where I have to do this.

models.py

from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Autor(models.Model):
    nombre = models.CharField(max_length=40)
    apellido = models.CharField(max_length=40)
    usuario = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
    def __str__(self):
        return "%s %s" % (self.nombre, self.apellido)

class Personaje(models.Model):
    nombre = models.CharField(max_length=40)
    personalidad = models.CharField(max_length=20)
    creador = models.ForeignKey(Autor, on_delete=models.CASCADE)
    def __str__(self):
        return "nombre: %s Creador: %s" % (self.nombre, self.creador)


class Historia(models.Model):
    autor = models.ForeignKey(Autor, on_delete=models.CASCADE)
    personajes = models.ManyToManyField(Personaje)
    desarrollo = models.TextField()

views.py

I'm not catching the request.user.id yet by simplicity (I'm really stuck with the filter thing) I know how to do that, hardcoding the user id will be just fine.

from django.shortcuts import render
from django.views.generic import CreateView
from core.models import Historia
# Create your views here.

class CrearHistoria(CreateView):
    model = Historia
    fields = ['personajes', 'desarrollo']
    template_name = 'core/crear_historia.html'

The current result:

What I have:

What I want to accomplish:

the red rectangle are the options that must appear

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Venguiat
  • 27
  • 7

2 Answers2

1

I have been struggling with this days but here is my solution.

from django.shortcuts import render
from django.views.generic import CreateView
from core.models import Historia, Personaje
# Create your views here.

class CrearHistoria(CreateView):
    model = Historia
    fields = ['personajes', 'desarrollo']
    template_name = 'core/crear_historia.html'
    def get_form(self, form_class=None):
        """Return an instance of the form to be used in this view."""
        if form_class is None:
            form_class = self.get_form_class()
            my_form_class = form_class(**self.get_form_kwargs())
            my_form_class['personaje'].field.queryset = Personaje.objects.filter(autor__id=2)
            return my_form_class

How I came to this solution: How do I filter ForeignKey choices in a Django ModelForm? Here I found that it's needed define the queryset attribute. And here (This web is awesome) I found that I can access the form instantiated by the CreateView.

But the hardest part was find the correct syntax to define the queryset so after search a lot I decided do it by my self:

def get_form(self, form_class=None):
    """Return an instance of the form to be used in this view."""
    if form_class is None:
        form_class = self.get_form_class()
        my_form_class = form_class(**self.get_form_kwargs())
        print("Las variables son: ", vars(my_form_class))
        print("Las variables son:", vars(my_form_class['personaje'].field))
        print("El valor del atributo es:", my_form_class['personaje'].field.queryset)
        my_form_class['personaje'].field.empty_label = '(No hay selecciones)'
        my_form_class['personaje'].field.queryset = Personaje.objects.filter(autor__id=2)
        return my_form_class

In case that you need filter by user you will pass the self.request.user.id value to the filter method.

Here is the prints outputs:

Las variables son: {'instance': , '_validate_unique': False, 'is_bound': False, 'data': {}, 'files': {}, 'auto_id': 'id_%s', 'initial': {}, 'error_class': , 'label_suffix': ':', 'empty_permitted': False, '_errors': None, 'fields': OrderedDict([('titulo', ), ('autor', ), ('personaje', ), ('desarrollo', )]), '_bound_fields_cache': {}, 'renderer': } Las variables son: {'empty_label': '---------', 'required': True, 'label': 'Personaje', 'initial': None, 'show_hidden_initial': False, 'help_text': '', 'disabled': False, 'label_suffix': None, 'localize': False, 'widget': , 'error_messages': {'required': 'Este campo es obligatorio.', 'invalid_choice': 'Seleccione una opción válida. La opción seleccionada no es una de las disponibles.'}, 'validators': [], '_queryset': , ]>, 'limit_choices_to': {}, 'to_field_name': 'id'} El valor del atributo es: , ]>

I hope that this could help anyone who needs a little modification in a EditView. Finally thanks for the previous answer but that's not was what I was looking for.

Venguiat
  • 27
  • 7
0

Specify the Form and add this clean method:

def clean_personajes(self):
    count = 5 # set count
    personajes = self.cleaned_data.get("personajes")
    if personajes.count() > count:
        raise forms.ValidationError("Please select at-most {}.".format(count))
    return personajes
Atley Varghese
  • 576
  • 4
  • 10