How to make a constructor through which the user can create a template by adding any field from the model to it, including nested fields?

Viewed 33

I have 5 entities that are related to each other. I have successfully described the models and the relationships between them. Everything is good here.

But they require a certain constructor from me, which assumes that the administrator can, in user mode (in the admin panel, or on any other page), change the appearance of the check that is printed for the client without changing the source code of the program (i.e. add or remove ticket fields).

For example, a user can create 2 receipt templates - "regular" and "reduced".

A "regular" check will include the name of the voyage, itinerary, carrier, departure_station and arrival_station, departure_date and arrival_date, place_number, etc., while "abbreviated" will only contain the name of the voyage, departure_date and place_number

I will briefly describe the models here (models.py):

class BusStation(models.Model):
    name = models.CharField(max_length=64)
    city = models.CharField(max_length=32)
    region = models.CharField(max_length=32)

class Carrier(models.Model):
    name = models.CharField(max_length=64)
    inn_number = models.PositiveBigIntegerField(unique=True)

class Itinerary(models.Model):
    name = models.CharField(max_length=64)
    number = models.CharField(max_length=64)
    departure_station = models.ForeignKey(BusStation, related_name='departure_busstations', on_delete=models.CASCADE)
    arrival_station = models.ForeignKey(BusStation, related_name='arrival_bus_stations', on_delete=models.CASCADE)

class Voyage(models.Model):
    departure_date = models.DateTimeField()
    arrival_date = models.DateTimeField()
    itinerary = models.ForeignKey(Itinerary, related_name='itineraries', on_delete=models.CASCADE)
    bus_station_platform = models.PositiveIntegerField()

class Ticket(models.Model):
    TYPE_TICKET = [
    ('1', '1'),
    ('2', '2'),
]

    passengers_name = models.CharField(max_length=128)
    voyage = models.ForeignKey(Voyage, related_name='voyages', on_delete=models.CASCADE)
    ticket_number = models.CharField(max_length=32, unique=True)
    place_number = models.PositiveSmallIntegerField()
    type_ticket = models.CharField(max_length=256, choices=TYPE_TICKET)

My form for creating a ticket is very simple, everything works correctly there (forms.py):

class TicketCreateForm(forms.ModelForm):
class Meta:
    model = Ticket
    fields = ['passengers_name', 'voyage', 'ticket_number', 'place_number', 'type_ticket']
    exclude = ['created', 'updated']

Accordingly, the handler is also very simple (views.py):

@staff_member_required
def ticket_create(request):
    if request.method == 'POST':
        form = TicketCreateForm(request.POST)
        if form.is_valid():
            ticket = form.save(commit=False)
            ticket.save()
            return render(request, 'tickets/ticket_created.html', {
                 'ticket': ticket,
            })
    else:
        form = TicketCreateForm()

    context = {
        'form': form,
    }

    return render(request, 'tickets/ticket_create.html', context)

I suppose that I need to create another entity and somehow put the fields of the Ticket model in it, but I'm not sure if this is correct.

Any help would be welcome, because I'm stuck on the implementation of the constructor, because I don't understand what approach to use to implement the logic.

1 Answers

views.py

from .forms import ReducedTicketForm, RegularTicketForm

class CreateTicketFormView(FormView):
    def get_form_class(self):
        """Return the form class to use."""
        if self.request.kwargs.get('ticket_type') == 'reduced':
            return ReducedTicketForm
        return RegularTicketForm

    # if there is a need to use different templates,
    # adjust the get_template_names() method.
    # This is just optional, as you can also use a single template.
    def get_template_names(self):
        if self.request.kwargs.get('ticket_type') == 'reduced':
            return 'reduced_ticket_form.html'
        if self.request.kwargs.get('ticket_type') == 'regular':
            return 'regular_ticket_form.html'
        return super().get_template_names()

    # your code ...

forms.py

class ReducedTicketForm(forms.ModelForm):
    class Meta:
        model = Ticket
        fields = ['passengers_name', 'voyage', 'place_number']
        exclude = ['created', 'updated']


class RegularTicketForm(forms.ModelForm):
    class Meta:
        model = Ticket
        fields = ['passengers_name', 'voyage', 'ticket_number', 'place_number', 'type_ticket']
        exclude = ['created', 'updated']

urls.py

from django.urls import path
from .views import CreateTicketFormView

urlpatterns = [
    path(
        "ticket/new/<str:ticket_type>/", CreateTicketFormView.as_view(), name="ticket-create"
    ),
]

template.html

<a href="{% url 'ticket-create' ticket_type='reduced' %}">
  Create a reduced ticket
</a>
<a href="{% url 'ticket-create' ticket_type='regular' %}">
  Create a regular ticket
</a>

<hr>

{{ form }}
Related