Hi guys, I am currently building a vocabulary learning app as a “first big private project” that I want to share when it’s done (I want to break into the industry somehow, so am building a portfolio)
for that I have some CBVs:
- IndexView(TemplateView)
- WordListView(ListView)
- LanguageWordListView(ListView)
- WordFormView(FormView)
and some models:
- AbstractWord()
- Language()
- Word()
I think the Word-model is the most important one:
class Word(models.Model):
'''The translated representations of each AbstractWord
word = the concept behind abstract
text = the word in the language we are looking for.
'''
word = models.ForeignKey(AbstractWord, to_field='abstract_word', on_delete=models.CASCADE)
language = models.ForeignKey(Language, on_delete=models.CASCADE)
text = models.CharField(max_length=100, blank=True)
definition = models.CharField(max_length=100, blank=True)
def __str__(self):
return f'{self.word}, {self.language}, {self.text}, {self.definition}'
as well as the follwing URL-patterns:
urlpatterns = [
path('', IndexView.as_view(), name='index'),
path('word-list/', WordListView.as_view(), name='word-list-view'),
path('word-list/<str:language>/', LanguageWordListView.as_view(), name='lang-word-list-view'),
path('word-list/<str:language>/<int:pk>', WordFormView.as_view(), name='word-form'),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
In the WordFormView, I want to forward to a random word, after answering for the meaning of the current word correctly, I have the following view, sorry it is quite long, by now:
class WordFormView(FormView):
'''shows the form to enter the meaning of the word.
Forwards to a random new word, once the word is answered correctly.
After three incorrect guesses, the word should be skipped
Todo 1: Implement logic for wrong answers
Todo 2: Implement logic for forwarding
'''
template_name = 'VocabTrainer/templates/word_form.html'
form_class = AnswerWord
success_url = None
def get_context_data(self, **kwargs):
'''set the context data accordingly - now we can use the current_word in word_form'''
context = super().get_context_data(**kwargs)
word_id = self.kwargs.get('pk')
language = self.kwargs.get('language')
context['current_word'] = Word.objects.get(pk=word_id)
return context
def form_valid(self, form):
'''checks whether the form is valid'''
# user_input defines that the field is the form field 'translation'
user_input = form.cleaned_data['translation']
# here we pass the current word from get_context_data into this function
current_word = self.get_context_data()['current_word']
if user_input == current_word.text:
#pick the current language
current_language = self.kwargs.get('language')
# if the user input is the correct value, forward to a random word:
all_words_except_current = Word.objects.filter(language=current_language).exclude(pk=current_word.pk)
if all_words_except_current.exists():
random_word = random.choice(all_words_except_current)
self.success_url = reverse('word-form', kwargs={'language':current_language, 'pk':random_word.pk})
else:
self.success_url = reverse('index')
return super().form_valid(form)
Upon forwarding, I get the following error:
Exception Type: ValueError at /word-list/german/2
Exception Value: Field ‘id’ expected a number but got ‘german’.
I get that I am forwarding in the self.success_url = reverse('word-form', kwargs={'language':current_language, 'pk':random_word.pk})
but I cannot get to the point where this is working as intended …
it should forward from
/word-list/german/2
to a randomly selected word-list/german/{random_word.pk}
where random word is picked from the subset of Words where the language is German.
What am I doing wrong?
Edit: solved - by changing the form_valid to the following
def form_valid(self, form):
'''checks whether the form is valid'''
# user_input defines that the field is the form field 'translation'
user_input = form.cleaned_data['translation']
# here we pass the current word from get_context_data into this function
current_word = self.get_context_data()['current_word']
current_language = current_word.language #this line is the solution
if user_input == current_word.text:
#pick the current language
# if the user input is the correct value, forward to a random word:
language_words = Word.objects.filter(language=current_language)
all_language_words_except_current = language_words.exclude(pk=current_word.pk)
if all_language_words_except_current.exists():
random_word = random.choice(all_language_words_except_current)
self.success_url = reverse('word-form', kwargs={'language':current_language, 'pk':random_word.pk})
return super().form_valid(form)