• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Zapisywanie zalogowanego użytkownika django

Object Storage Arubacloud
0 głosów
288 wizyt
pytanie zadane 7 marca 2020 w Python przez michaloxs Początkujący (280 p.)

Witam. Robię sobie ostatnio aplikację, w której zalogowany użytkownik może tworzyć ankiety, przeglądać zrobione ankiety, itd. Wszystko działało ok, do momentu w którym stwierdziłem, że chcę mieć dynamiczne formy. Nie byłem pewny jak to zrobić, ostatecznie zrobiłem coś takiego:

forms.py

class CreateChoiceForm(forms.ModelForm):

    choice_0 = forms.CharField(required=True)
    choice_1 = forms.CharField(required=True)

    class Meta:
        model = Question
        fields = ['question_text']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        choice = Choice.objects.filter(
            question=self.instance
        )
        for i in range(len(choice) + 1):
            field_name = 'choice_%s' % (i,)
            self.fields[field_name] = forms.CharField(required=False)
            try:
                self.initial[field_name] = choice[i].choice
            except IndexError:
                self.initial[field_name] = ""
            field_name = 'choice_%s' % (i+1,)
            self.fields[field_name] = forms.CharField(required=False)

    def save(self, commit=True):
        question = self.instance
        question.question_text = self.cleaned_data['question_text']
        question.choice_set.all().delete
        question.save()
        for i in range(2):
            choice = self.cleaned_data['choice_%i' % (i)]
            Choice.objects.create(question=question, choice_text=choice)

    def get_interest_fields(self):
        for field_name in self.fields:
            if field_name.startswith('choice_'):
                yield self[field_name]

Problem w tym, że teraz kiedy próbuję zatwierdzić wypełnione formularze na stronie to pojawia się ten błąd:

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/polls/createPoll/

Django Version: 2.2.5
Python Version: 3.6.10
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'bootstrap3',
 'pollapp']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in _execute
  84.                 return self.cursor.execute(sql, params)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\sqlite3\base.py" in execute
  383.         return Database.Cursor.execute(self, query, params)

The above exception (NOT NULL constraint failed: pollapp_question.author_id) was the direct cause of the following exception:

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\core\handlers\base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\core\handlers\base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\views\generic\base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\contrib\auth\mixins.py" in dispatch
  52.         return super().dispatch(request, *args, **kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\views\generic\base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\views\generic\edit.py" in post
  172.         return super().post(request, *args, **kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\views\generic\edit.py" in post
  142.             return self.form_valid(form)

File "H:\conda\INZ\pollapp\views.py" in form_valid
  71.         poll = form.save()

File "H:\conda\INZ\pollapp\forms.py" in save
  51.         question.save()

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\base.py" in save
  741.                        force_update=force_update, update_fields=update_fields)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\base.py" in save_base
  779.                 force_update, using, update_fields,

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\base.py" in _save_table
  870.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\base.py" in _do_insert
  908.                                using=using, raw=raw)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\manager.py" in manager_method
  82.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\query.py" in _insert
  1186.         return query.get_compiler(using=using).execute_sql(return_id)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
  1335.                 cursor.execute(sql, params)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in execute
  99.             return super().execute(sql, params)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in execute
  67.         return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in _execute_with_wrappers
  76.         return executor(sql, params, many, context)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in _execute
  84.                 return self.cursor.execute(sql, params)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\utils.py" in __exit__
  89.                 raise dj_exc_value.with_traceback(traceback) from exc_value

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\utils.py" in _execute
  84.                 return self.cursor.execute(sql, params)

File "C:\Users\MaineKomputere\Anaconda3\envs\MyDjangoEnv\lib\site-packages\django\db\backends\sqlite3\base.py" in execute
  383.         return Database.Cursor.execute(self, query, params)

Exception Type: IntegrityError at /polls/createPoll/
Exception Value: NOT NULL constraint failed: pollapp_question.author_id

Tutaj reszta kodu:

models.py

from django.db import models
from django.utils import timezone
import datetime
from django.contrib import auth
# Create your models here.

User = auth.get_user_model()

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=False)


    def __str__(self):
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    def __str__(self):
        return self.choice_text

class User(auth.models.User, auth.models.PermissionsMixin):

    def __str__(self):
        return "@{}".format(self.username)

views.py

class CreatePoll(LoginRequiredMixin, generic.CreateView):
    form_class = forms.CreateChoiceForm
    template_name = 'polls/createPoll.html'
    success_url = reverse_lazy('pollapp:index')

    def form_valid(self, form):
        poll = form.save()
        poll.author = self.request.user
        poll.save()
        return super().form_valid(form)

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.request = self.request
        return form

    def get_success_url(self):
        return reverse('pollapp:myPolls', kwargs={'pk': self.object.pk})

Zmiana

author = models.ForeignKey(User, on_delete=models.CASCADE, null=False)

na

author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)

niewiele daje. Wtedy pojawia się błąd:

Exception Value: 'NoneType' object has no attribute 'author' django

No i nie za bardzo wiem co teraz robić. Bardzo prosiłbym o pomoc w rozwiązaniu tego problemu, bo trochę stanąłem w martwym punkcie.

1 odpowiedź

+2 głosów
odpowiedź 7 marca 2020 przez adrian17 Ekspert (344,860 p.)

Strzelam: czy nie zabrakło aby pola na autora w Twoim formularzu?

Alternatywnie, możesz przed zapisaniem w bazie dodać do obiektu:

question = form.save(commit=False)
question.author = self.request.user
question.save()
komentarz 7 marca 2020 przez michaloxs Początkujący (280 p.)

Zanim zacząłem się bawić w dynamiczny formularz to nie potrzebowałem pola author w formularzu (przynajmiej tak mi się wydaje ;P) i działało. Zresztą i tak dodałem, żeby sprawdzić, czy zadziała i dalej jest ten sam błąd. Właśnie w kodzie mam zapisane to co napisałeś w metodzie form_valid(), wygląda to u mnie tak:

    def form_valid(self, form):
        poll = form.save(commit=False)
        poll.author = self.request.user
        poll.save()
        return super().form_valid(form)

 

komentarz 7 marca 2020 przez adrian17 Ekspert (344,860 p.)
Wydaje mi się, że w tym momencie wywołanie `super().form_valid(form)` już za bardzo nie ma sensu (bo w końcu niezależnie spróbuje drugi raz stworzyć obiekt bazodanowy).
komentarz 7 marca 2020 przez michaloxs Początkujący (280 p.)
W sumie racja, to pozostałość po starej wersji, zapomniałem to usunąć. Jednak usunięcie nie naprawia problemu :(
komentarz 7 marca 2020 przez adrian17 Ekspert (344,860 p.)
To pokaż proszę aktualny kod.
komentarz 7 marca 2020 przez michaloxs Początkujący (280 p.)
edycja 7 marca 2020 przez michaloxs

forms.py

class CreateChoiceForm(forms.ModelForm):

    choice_0 = forms.CharField(required=True)
    choice_1 = forms.CharField(required=True)

    class Meta:
        model = Question
        fields = ['question_text', 'author']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        choice = Choice.objects.filter(
            question=self.instance
        )
        for i in range(len(choice) + 1):
            field_name = 'choice_%s' % (i,)
            self.fields[field_name] = forms.CharField(required=False)
            try:
                self.initial[field_name] = choice[i].choice
            except IndexError:
                self.initial[field_name] = ""
            field_name = 'choice_%s' % (i+1,)
            self.fields[field_name] = forms.CharField(required=False)

    def save(self, commit=True):
        question = self.instance
        question.question_text = self.cleaned_data['question_text']
        question.choice_set.all().delete
        question.save()
        for i in range(2):
            choice = self.cleaned_data['choice_%i' % (i)]
            Choice.objects.create(question=question, choice_text=choice)

    def get_interest_fields(self):
        for field_name in self.fields:
            if field_name.startswith('choice_'):
                yield self[field_name]

views.py

class CreatePoll(LoginRequiredMixin, generic.CreateView):
    form_class = forms.CreateChoiceForm
    template_name = 'polls/createPoll.html'
    success_url = reverse_lazy('pollapp:index')

    def form_valid(self, form):
        poll = form.save(commit=False)
        poll.author = self.request.user
        poll.save()


    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.request = self.request
        return form

    def get_success_url(self):
        return reverse('pollapp:myPolls', kwargs={'pk': self.object.pk})

models.py

from django.db import models
from django.utils import timezone
import datetime
from django.contrib import auth
# Create your models here.

User = auth.get_user_model()

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=False)


    def __str__(self):
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    def __str__(self):
        return self.choice_text

class User(auth.models.User, auth.models.PermissionsMixin):

    def __str__(self):
        return "@{}".format(self.username)

urls.py

from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from . import views

app_name = 'pollapp'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('createdPolls/', views.ListView.as_view(), name='createdPolls'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
    path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    path('signup/', views.SignUp.as_view(), name='signup'),
    path('createPoll/', views.CreatePoll.as_view(), name='createPoll'),
    path('<int:pk>/choice', views.createChoice, name='createChoice'),
    path('userPolls/', views.UserPolls.as_view(), name='userPolls'),
]

To chyba wszystko co potrzebne.

komentarz 8 marca 2020 przez adrian17 Ekspert (344,860 p.)

...chwila, dopiero teraz zauważyłem.

poll = form.save(commit=False)

Fajnie że to zrobiłeś, ale...

    def save(self, commit=True):
        question = self.instance
        question.question_text = self.cleaned_data['question_text']
        question.choice_set.all().delete
        question.save()
        for i in range(2):
            choice = self.cleaned_data['choice_%i' % (i)]
            Choice.objects.create(question=question, choice_text=choice)

Co z tego, skoro napisałeś własną implementację save() która kompletnie ignoruje argument commit? :P

A skoro masz kompletnie własny save(), to w ogóle nie rozumiem gdzie masz problem, skoro możesz po prostu przekazać `author` do formularza i ustawić go w save().

komentarz 8 marca 2020 przez michaloxs Początkujący (280 p.)

O kurcze faktycznie :D

Dzięki za pomoc, sam bym pewnie tego nie zauważył ;P

Poniżej zostawiam kod, który działa, w razie jakby ktoś szukał odpowiedzi.

views.py

class CreatePoll(LoginRequiredMixin, generic.CreateView):
    form_class = forms.CreateChoiceForm
    template_name = 'polls/createPoll.html'
    success_url = reverse_lazy('pollapp:index')

    def get_form_kwargs(self):
        kwargs = super(CreatePoll, self).get_form_kwargs()
        kwargs.update({'request': self.request})
        return kwargs

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.request = self.request
        return form

    def get_success_url(self):
        # return reverse('pollapp:myPolls', kwargs={'pk': self.object.pk})
        return reverse('pollapp:index')

forms.py

class CreateChoiceForm(forms.ModelForm):

    choice_0 = forms.CharField(required=True)
    choice_1 = forms.CharField(required=True)

    class Meta:
        model = Question
        fields = ['question_text',]

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

        choice = Choice.objects.filter(
            question=self.instance
        )
        for i in range(len(choice) + 1):
            field_name = 'choice_%s' % (i,)
            self.fields[field_name] = forms.CharField(required=False)
            try:
                self.initial[field_name] = choice[i].choice
            except IndexError:
                self.initial[field_name] = ""
            field_name = 'choice_%s' % (i+1,)
            self.fields[field_name] = forms.CharField(required=False)

    def save(self, commit=True):
        question = self.instance
        question.question_text = self.cleaned_data['question_text']
        question.author = self.request.user
        question.choice_set.all().delete
        question.save()
        for i in range(2):
            choice = self.cleaned_data['choice_%i' % (i)]
            Choice.objects.create(question=question, choice_text=choice)

    def get_interest_fields(self):
        for field_name in self.fields:
            if field_name.startswith('choice_'):
                yield self[field_name]

 

Podobne pytania

0 głosów
1 odpowiedź 252 wizyt
0 głosów
1 odpowiedź 214 wizyt
0 głosów
1 odpowiedź 65 wizyt

92,579 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,964 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...