Search code examples
pythonhtmldjangodjango-forms

Django Form, how to get further model info for ModelChoiceField


I have 3 models that are related to eachother. A showing has the 'film' and 'screen' foreignkeys that link to Film and Screen. I'm trying to setup a webpage so that you can view information on a showing that is already setup, and edit it from dropdowns of what films and screens are available.

The problem I have at the moment is that instead of showing the screen numbers, and film titles that can be selected from, my dropdowns are showing 'Screen Object (4)' or 'Film Object (2)'. How do I get that next level of information to show from each object?

My models are:

class Film(models.Model):
    ageRatings =[
        ("U", "U"),
        ("PG", "PG"),
        ("12", "12"),
        ("15", "15"),
        ("18", "18"),
    ]

    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=255)
    length = models.DurationField()
    rating = models.CharField(max_length=3, choices=ageRatings, default="U")
    genre = models.CharField(max_length=50)
    poster_url = models.CharField(max_length=255, default='')
    
    
class Screen(models.Model):
    id = models.AutoField(primary_key=True)
    cinema = models.ForeignKey(Cinema, on_delete=models.CASCADE, related_name='screens')
    screen_number = models.PositiveSmallIntegerField()
    seating_capacity = models.PositiveIntegerField()
    

class Showing(models.Model):
    id = models.AutoField(primary_key=True)
    film = models.ForeignKey(Film, on_delete=models.CASCADE, related_name='showings')
    screen = models.ForeignKey(Screen, on_delete=models.CASCADE, related_name='showings')
    start_time = models.DateTimeField()
    end_time = models.DateTimeField(null=True)
    available_seats = models.IntegerField()
    adult_price = models.FloatField()
    student_price = models.FloatField()
    child_price = models.FloatField()
    
    def save(self, *args, **kwargs):
        self.end_time = self.start_time + self.film.length
        super().save(*args, **kwargs)

    def __str__(self):
        out = self.start_time.strftime("%d %B %Y %I:%M%p")
        out = out + " - " + self.film.title
        print(out)
        return out

My form is:

class ShowingForm(forms.ModelForm):
    screen = forms.ModelChoiceField(queryset=Screen.objects.all().order_by('screen_number'))
    film = forms.ModelChoiceField(queryset=Film.objects.all().order_by('title'))
    class Meta:
        model = Showing
        fields = ['screen', 'film', 'start_time']

My HTML is:

{% extends 'base.html' %}

{% block content %}

<div class="container mt-4">
  <h1>Update Showing</h1>
  <form method="post" action="{% url 'update_showing' showing.id %}">
    {% csrf_token %}
    {% for field in form %}
      <div class="form-group">
        {{ field.label_tag }}
        {{ field }}
      </div>
    {% endfor %}
    <button type="submit" class="btn btn-primary">Update Showing</button>
  </form>
</div>

{% endblock %}

Solution

  • Django searches for the string representation of Screen and Film. You have to define it the same way you did for Showing by implementing the __str__() method. Then this representation is going to replace "Screen Object (4)"

    class Film(models.Model):
        ageRatings =[
            ("U", "U"),
            ("PG", "PG"),
            ("12", "12"),
            ("15", "15"),
            ("18", "18"),
        ]
    
        id = models.AutoField(primary_key=True)
        title = models.CharField(max_length=255)
        length = models.DurationField()
        rating = models.CharField(max_length=3, choices=ageRatings, default="U")
        genre = models.CharField(max_length=50)
        poster_url = models.CharField(max_length=255, default='')
    
    def __str__(self):
        return self.title
        
    class Screen(models.Model):
        id = models.AutoField(primary_key=True)
        cinema = models.ForeignKey(Cinema, on_delete=models.CASCADE, related_name='screens')
        screen_number = models.PositiveSmallIntegerField()
        seating_capacity = models.PositiveIntegerField()
    
    def __str__(self):
        return self.screens.name  # adjust this to your the logic of Cinema model