Джанго - Форма не сохраняет

Я делаю веб-сайт, который показывает события, используя ListView, и каждое событие имеет форму регистрации.

Все ошибки проверки формы отображаются, но при успешной отправке формы она просто приводит меня на страницу успеха, и форма не сохраняется в базе данных.

Когда я отправляю форму через администратора, а не форму, она выдает мне сообщение об ошибке: NameError в /admin/home/signups/add/ global name 'fullname' не определено с помощью traceback to: instance.fullname = fullname в forms.py,

Другая ошибка, если я полностью удаляю эту строку: NameError в /admin/home/signups/add/ global name 'request' не определяется с помощью traceback to instance.ip = get_ip (request) в forms.py.

forms.py:

from django import forms
from .models import SignUps, Hours, Events
import datetime
from ipware.ip import get_ip

class SignUpForm(forms.ModelForm):
    fullname = forms.CharField(label="Full name", widget=forms.TextInput(attrs={'placeholder': 'Full name', 'class': 'form-control'}))

    class Meta:
        model = SignUps
        fields = ['eventname','fullname','ip']

    def clean_fullname(self):
        fullname = self.cleaned_data.get('fullname').title()
        eventname = self.cleaned_data.get('eventname')
        try:
            name = Hours.objects.get(fullname=fullname)
        except Hours.DoesNotExist:
            raise forms.ValidationError("Please enter a valid Key Club member's full name as displayed in the hours page.")
        try:
            name = SignUps.objects.get(fullname=fullname)
        except SignUps.DoesNotExist:
            try:
                numOfSignUps = SignUps.objects.filter(eventname=eventname).count()
            except SignUps.DoesNotExist:
                numOfSignUps = 0
            try:
                event = Events.objects.get(name=eventname)
            except Events.DoesNotExist:
                raise forms.ValidationError("Something went wrong. This event does not exist.")
            try:
                ifFull = Events.objects.filter(name=eventname).get(maximum__gt=numOfSignUps)
            except Events.DoesNotExist:
                raise forms.ValidationError("The maximum number of attendees has already been reached.")
            try:
                date = Events.objects.filter(name=eventname).get(date=datetime.date.today())
            except Events.DoesNotExist:
                return fullname
            raise forms.ValidationError("It is too late to sign up for this event.")
        raise forms.ValidationError("This member is already signed up.")

    def save(self, commit=True):
        instance =  super(SignUpForm, self).save(commit=False)
        instance.fullname = fullname
        instance.ip = get_ip(request)
        if commit:
            instance.save()
        return instance

events / index.html основной текст:

<div class="container" style="margin-top:75px;">

    {% block content %}

    {% for announcements in announcement %}
    <div class="alert alert-info" role="alert">
      <h4 class="alert-heading">{{ announcements.announcementname }}</h4>
      <p style="margin:0 0;">{{ announcements.announcement | safe | linebreaksbr | urlize }}</p>
    </div>
    {% endfor %}

    {% for events in events_list %}
    <div id="checkIn" class="modal fade">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
            <h4 class="modal-title">OCC Check-Ins</h4>
          </div>
          <div class="modal-body">
            <form method="POST">
              <div class="form-group">
                <input type="password" class="form-control" id="passcode" placeholder="Passcode" maxlength="4" autocomplete="off">
              </div>
              <div class="form-group"><button type="submit" class="btn btn-primary btn-block">Sign in</button></div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->

    <div class="card card-block col-sm-6" style="display:inline-block;">
      <div class="dropdown" style="float:right;">
        <a href="#" style="color:black;" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><h5><i class="fa fa-caret-down" aria-hidden="true"></i></h5></a>
        <div class="dropdown-menu dropdown-menu-left" aria-labelledby="dropdownMenuButton" style="right:0;left:auto;">
          <a class="dropdown-item" href="#">View</a>
        </div>
      </div>
      <h4 class="card-title">{{ events.name }}</h4>
      <p class="card-text">Description: <span class="text-muted">{{ events.description }}</span><br>Where: <a href="https://www.google.com/maps/place/{{ events.location }}" class="text-muted">{{ events.location }}</a><br>When: <span class="text-muted">{{ events.date|date:"D, M d, Y" }}</span><br>Time: <span class="text-muted">{{ events.time|time:"P" }}</span><br>Max: <span class="text-muted">{{ events.maximum }}</span><br>Hours: <span class="text-muted">{{ events.hours }}</span><br>Check in with: <span class="text-muted">{{ events.occ }}</span></p>
      <h4 class="card-text" style="margin-top:-6px;margin-bottom:8px;"><a href="#" style="color:black; text-decoration:none;" data-toggle="modal" data-target="#checkIn"><i class="fa fa-sign-in" aria-hidden="true" style="margin-bottom:10px;"></i> OCC Check-Ins</a></h4>
      <form action="/events/" class="form" method="POST">{% csrf_token %}
        <div class="form-group">
          <input id="id_eventname" maxlength="125" name="eventname" type="hidden" value="{{ events.name }}">
          {{ form.fullname }}
          {{ form.fullname.errors }}
        </div>
        <button class="btn btn-primary btn-block" type="submit">Sign up</button>
      </form>
    </div>
    {% endfor %}
    {% endblock %}

  </div>

success.html - это просто пустая HTML-страница с надписью "success":

<div class="container" style="margin-top:75px;">
    Success
  </div>

views.py:

from django.shortcuts import render
from django.views.generic import ListView, FormView
from django.views.generic.detail import SingleObjectMixin
from home.models import Events, Announcement, Hours, SignUps
from django import forms
from .forms import SignUpForm
from django.http import HttpResponseForbidden
from django.urls import reverse
from django.views import View
from ipware.ip import get_ip
from django.views.generic.edit import FormView

# Create your views here.
def index(request):
    return render(request, 'home/index.html')

def about(request):
    return render(request, 'about/index.html')

def success(request):
    return render(request, 'events/success.html')

class EventsDisplay(ListView, FormView):
    template_name='events/index.html'
    context_object_name = "events_list"
    queryset = Events.objects.all().order_by("date")
    form_class = SignUpForm
    success_url = "/events/success"

    def get_context_data(self, **kwargs):
        self.object_list = self.get_queryset()
        context = super(EventsDisplay, self).get_context_data(**kwargs)
        context['announcement'] = Announcement.objects.all().order_by("-datetime")
        context['signup'] = SignUps.objects.all().order_by("fullname")
        return context

class HoursList(ListView):
    template_name = 'hours/index.html'
    context_object_name = "hours_list"
    queryset = Hours.objects.all().order_by("fullname")

    def get_context_data(self, **kwargs):
        context = super(HoursList, self).get_context_data(**kwargs)
        context['announcement'] = Announcement.objects.all().order_by("-datetime")
        return context

urls.py:

from django.conf.urls import url, include
from views import EventsDisplay, HoursList
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^events/$', EventsDisplay.as_view()),
    url(r'^events/success$', views.success, name='success'),
    url(r'^hours/$', HoursList.as_view()),
    url(r'^about/$', views.about, name='about'),
]

2 ответа

Решение

Итак, после тщательного изучения кода, я думаю, что здесь может быть сделано гораздо больше рефакторинга.

Во-первых, я не вижу смысла смешивать представления на основе классов и представления на основе функций. Эти стандартные get а также post запросы можно сделать так:

from django.shortcuts import render
from django.views.generic import ListView, FormView
from django.views.generic.detail import SingleObjectMixin
from home.models import Events, Announcement, Hours, SignUps
from django import forms
from .forms import SignUpForm
from django.http import HttpResponseForbidden
from django.urls import reverse
from django.views import View
from ipware.ip import get_ip
from django.views.generic.edit import FormView

class EventsDisplay(ListView, FormView):
    template_name='events/index.html'
    context_object_name = "events_list"
    queryset = Events.objects.all().order_by("date")
    form_class = SignUpForm 
    success_url = "events/success.html"

    def get_context_data(self, **kwargs):
        context = super(EventsDisplay, self).get_context_data(**kwargs)
        context['announcement'] = Announcement.objects.all().order_by("-datetime")
        # No need to insert 'form' as Django  takes care of that
        # by utilising self.form_class
        return context

    def form_valid(self, form): 
        instance = form.save(commit=False) instance.save() 
        return super(EventsDisplay, self).form_valid(form)

По сути, я объединил ваш код в одно представление на основе классов, которое наследуется от ListView (для тебя get запросы) и FormView (для тебя post Запросы).

В результате этого изменения в ваших взглядах ваш urls теперь должно стать:

from django.conf.urls import url, include
from views import EventsList, HoursList
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^events/$', EventsDisplay.as_view()),
    url(r'^hours/$', HoursList.as_view()),
    url(r'^about/$', views.about, name='about'),
]

Наконец, если вам нужно "подтолкнуть" переменные модели перед сохранением (например, вставить IP-адрес), вы должны поместить такой код в форму, например:

from django import forms
from .models import SignUps, Hours, Events
import datetime
from ipware.ip import get_ip
class SignUpForm(forms.ModelForm):
    fullname = forms.CharField(label="Full name", widget=forms.TextInput(attrs={'placeholder': 'Full name', 'class': 'form-control'}))

    class Meta:
        model = SignUps
        fields = ['eventname','fullname','ip']

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(SignUpForm, self).__init__(*args, **kwargs)

    def clean_fullname(self):
        ...

    def save(self, commit=True):
        instance =  super(SignUpForm, self).save(commit=False)
        instance.fullname = fullname
        instance.ip = get_ip(self.request)
        if commit:
            instance.save()
        return instance

Когда вы нажмете ошибку проверки в SignUpForm, ваш SignUp(request) Функция наверняка подхватит ошибки в form объект, но вы возвращаете другую страницу (events/success.html) из чего твой GET возвращается (events/index.html).

Я предполагаю что events/success.html не имеет тот же HTML-код, который вы предоставили, который, кажется, для events/index.html,

Ваш SignUp(request) функция должна выглядеть так:

def SignUp(request):
    if request.method=='POST':
        form = SignUpForm(request.POST or None)
        if form.is_valid():
            instance = form.save(commit=False)
            fullname = form.cleaned_data.get("fullname")
            instance.fullname = fullname
            instance.ip = get_ip(request)
            instance.save()
        else: # INVALID FORM - WE HAVE VALIDATION ERRORS SO RETURN SAME PAGE.
            return render(request, "events/index.html", {"form": form})

    else:
        form = SignUpForm
    context = {
        "form": form,
    }
    return render(request, "events/success.html", context)
Другие вопросы по тегам