Django CreateView not sending form - django

This view is not sending the form. I don't know why. I can see it is not sending the form cause I'm printing the context at the end of the get_context_data function.
class CrearFeralSpirit(CreateView):
template_name = "hisoka/crear_feral_spirit.html"
model = FeralSpirit
fields = ['tipo', 'nombre', 'url']
def form_valid(self, form):
fireball = Fireball.objects.get(slug=self.kwargs.get('slug'))
form.instance.fireball = fireball
return super(CrearFeralSpirit, self).form_valid(form)
def get_context_data(self, *args, **kwargs):
context = super(CrearFeralSpirit, self).get_context_data()
fireball = Fireball.objects.get(slug=self.kwargs['slug_fireball'])
context['fireball'] = fireball
print context # Here I print the context, no form in it.
return context

As I put in the comment, you forgot to pass *args and **kwargs to the parent class when you call super, so it should be:
context = super(CrearFeralSpirit, self).get_context_data(*args, **kwargs)
*args and **kwargs are the parameters defined by django get_context_data and they are definitely used inside django. If you don't pass them to the parent class, django is lack of certain information that's needed. Without them django couldn't construct the form thus your context doesn't have any form.

Related

Passing kwargs from CBV to Form in Django

I have a ModelForm which needs a user passed in so that the queryset can be updated. I am overriding the __init__ method of the ModelForm as such:
def __init__(self, *args, **kwargs):
# override init to get user's casino's EmployeeType queryset
self.user = kwargs.pop('user')
print(self.user)
super(MemoForm, self).__init__(*args, **kwargs)
self.fields['receiver'].queryset = EmployeeType.objects.filter(
casino=self.user.casino
)
In the View I have a get and a post method. I am trying to pass the **kwargs in as such:
class VideoUploadView(LoginRequiredMixin, View):
"""
Display a form for uploading videos.
"""
form_class = VideoUploadForm
success_url = '/videos'
template_name = 'videos/video_upload.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(
request,
self.template_name,
{'form': form, 'user': self.request.user}
)
In a CreateView you are able to use the get_form_kwargs method to pass in the **kwargs. How is it done in a normal View? Should we use the __init__ method? The way shown above does not seem to work as both *args and **kwargs seem to be empty.
These are the built-in methods of View.
I don't really understand why you're not using a FormView here as well, so that you can still override get_form_kwargs; you really shouldn't ever need to define get (or post) directly.
But nevertheless, the answer is simple: you just pass your kwargs directly to the form:
form = self.form_class(user=request.user)

How to define the slug of another model in myListView

I sort of need help understanding my own code specifically the views.py. I'm trying to change url pattern for my TitleUpdateListView from using my Update models title field and instead using the slug field instead.
If someone could help explain line by line whats going in in my TitleUpdateListView so I could better understand whats specifically going on that would be great.
urls.py
urlpatterns = [
# Update view for each game
path('<str:title>/updates/', TitleUpdateListView.as_view(), name='title-updates'),
# Adds the ability to sort by platform
path('<str:title>/updates/<int:platform_id>/', TitleUpdateAjaxListView.as_view(), name='title-updates-ajax'),
]
views.py
class TitleUpdateListView(ListView):
model = Update
context_object_name = 'updates'
template_name = 'updates/title_updates.html'
def get_queryset(self):
title = get_object_or_404(Game, title=self.kwargs.get('title'))
return Update.objects.filter(game=title).order_by('-date_published')
def get_context_data(self, **kwargs):
context = super(TitleUpdateListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, title=self.kwargs.get('title'))
return context
class TitleUpdateAjaxListView(ListView):
model = Update
template_name = 'updates/updates_ajax.html'
context_object_name = 'updates'
paginate_by = 5
def get_queryset(self):
title = get_object_or_404(Game, title=self.kwargs.get('title'))
return Update.objects.filter(game=title, platform=Platform.objects.filter(
id=self.kwargs.get('platform_id')).first()).order_by('-date_published')
def get_context_data(self, **kwargs):
context = super(TitleUpdateAjaxListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, title=self.kwargs.get('title'))
return context
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
context = self.get_context_data()
return render(request, self.template_name, context)
Not sure what you meant by "I'm trying to change url pattern for my TitleUpdateListView from using my Update models title field and instead using the slug field instead.". In the urls.py, you can change the name of the parameter (the xxxx in <str:xxxx>) to whatever you want, as long as you also look for this same name in the view. You can change it to <str:slug> and in your view you'd fetch it like self.kwargs.get('slug'). Just remember to also change which parameter are you using to filter the Game table (slug instead of title).
As for explaining what your view does, you should probably take a look at Django's docs on Class Based Views, but I'll try to give an overview:
The get_queryset method is searching the Game table to find the games whose title matches the title passed in the URL parameter. It then returns a list of all Update objects whose game field points to the game just found.
The get_context_data method is adding the same Game object found in the get_queryset method to the view's context under the 'game' key. This means that you can access the Game object inside the template that this view renders.
You just need to change the get_queryset method of your view:
# change url variable name from title to slug
path('<str:slug>/updates/', TitleUpdateListView.as_view(), name='title-updates'),
def get_queryset(self):
# the url variables are stored in the dictionary self.kwargs
slug = self.kwargs.get('slug')
game = get_object_or_404(Game, slug=slug)
return Update.objects.filter(game=game).order_by('-date_published')
The same applies for get_context_data:
def get_context_data(self, **kwargs):
context = super(TitleUpdateListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, slug=self.kwargs.get('slug'))
return context

Django - FormView combined with Mixin

I have to specify a success_url, otherwise I get an error. So how to specify it, in order to stay to the same page?
Also, is everything else correct regarding the SearchView, beucase I have a feeling that something is missing. My context should be composed by form, query, concepts, language and languages.
Thanks
urls.py
url(r'^(?P<langcode>[a-zA-Z-]+)/search/$', SearchView.as_view(), name='search').
views.py
class _LanguageMixin(object):
def dispatch(self, request, *args, **kwargs):
self.langcode = kwargs.pop("langcode")
self.language = get_object_or_404(Language, pk=self.langcode)
return super(_LanguageMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(_LanguageMixin, self).get_context_data(**kwargs)
context.update({"language": self.language,
"languages": Language.objects.values_list('code',
flat=True)})
return context
class SearchView(_LanguageMixin, FormView):
template_name = "search.html"
form_class = SearchForm
success_url = #......
query = ''
concepts = []
def get_initial(self):
return {'langcode': self.langcode}
def get_context_data(self, **kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context.update({"query": self.query, "concepts": self.concepts})
return context
def form_valid(self, form):
self.query = form.cleaned_data['query']
self.concepts = # here is a long DB query; function(query)
return super(SearchView, self).form_valid(form)
[EDIT]
I did this:
def get_success_url(self):
return reverse('search', kwargs={'langcode': self.langcode})+"?query={}".format(self.query)
The form renders, but whenever I search for anything, I get back the empty search text field. And the URL looks something like this: http://localhost:8000/en-US/search/?query=asd
By default, a FormView (actually, any subclass of ProcessFormView) will return a HttpResponseRedirect in form_valid. As you are calling the super's method in your form_valid method, you also return a HttpResponseRedirect. In the process, the actual POST data is lost, and though you pass it as a GET parameter, it is not used in the actual form.
To fix this, you need to not call super in your form_valid method, but instead return a rendered template in a HttpResponse object, e.g.:
def form_valid(self, form):
self.query = form.cleaned_data['query']
self.concepts = # here is a long DB query; function(query)
return self.render_to_response(self.get_context_data(form=form))

How to pass ForeignKey value into initial data for Django form

I have a model like this:
class Job(models.Model):
slug = models.SlugField()
class Application(models.Model):
job = models.ForeignKey(Job)
And a view like this:
class ApplicationCreateView(CreateView):
model = Application
A user will view the job object (/jobs/<slug>/), then complete the application form for the job (/jobs/<slug>/apply/).
I'd like to pass application.job.slug as the initial value for the job field on the application form. I'd also like for the job object to be put in context for the ApplicationCreateView (to tell the user what job they're applying for).
How would I go about doing this in my view?
You may be interested in CreateView page of the fantastic http://ccbv.co.uk/ In this page, you can see in one glance which member methods and variables you can use.
In your case, you will be interested to override:
def get_initial(self):
# Call parent, add your slug, return data
initial_data = super(ApplicationCreateView, self).get_initial()
initial_data['slug'] = ... # Not sure about the syntax, print and test
return initial_data
def get_context_data(self, **kwargs):
# Call parent, add your job object to context, return context
context = super(ApplicationCreateView, self).get_context_data(**kwargs)
context['job'] = ...
return context
This has not been tested at all. You may need to play with it a little. Have fun.
I ended up doing the following in a function on my class:
class ApplicationCreateView(CreateView):
model = Application
form_class = ApplicationForm
success_url = 'submitted/'
def dispatch(self, *args, **kwargs):
self.job = get_object_or_404(Job, slug=kwargs['slug'])
return super(ApplicationCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
#Get associated job and save
self.object = form.save(commit=False)
self.object.job = self.job
self.object.save()
return HttpResponseRedirect(self.get_success_url())
def get_context_data(self, *args, **kwargs):
context_data = super(ApplicationCreateView, self).get_context_data(*args, **kwargs)
context_data.update({'job': self.job})
return context_data

Django - Generic View Subclassed - url Parameters

I need to display a detail page for a video with some other data.
For that I use DetailView that I have overridden to add some variables to the context.
Here are the code parts:
#urlconf
#...
(r'viewtube/(?P<pk>\d+)$', VideoFileDetailView.as_view()),
#...
#view
class VideoFileDetailView(DetailView):
model = VideoFile
def get_context_data(self, **kwargs):
context = super(VideoFileDetailView, self).get_context_data(**kwargs)
# context['rates'] = VideoRate.objects.filter(video=11, user=1)
return context
Here pk is the id of a video, I need to get the rates of the selected video by the current user.
It would have been useful to show the models. But I think you need to override get(), not get_context_data, as unfortunately the latter doesn't get passed the request, which you need in order to get the user. So:
def get(self, request, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
context['rates'] = VideoRate.objects.filter(video=self.object, user=request.user)
return self.render_to_response(context)
The request should be accessible at self.request. self.request is set at the beginning of the request (in View.dispatch) and should be available any of the subclass methods.
class VideoFileDetailView(DetailView):
model = VideoFile
def get_context_data(self, **kwargs):
context = super(VideoFileDetailView, self).get_context_data(**kwargs)
context['rates'] = VideoRate.objects.filter(video=11, self.request.user)
# note that the object is available via self.object or kwargs.get("object")
return context