I'm using Django-registration-redux and I want give more data to a view to render my base template. I read the example in doc.
My url.py:
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# context['book_list'] = Book.objects.all() # example in doc
context_dict = services.get_base_data_for_views(request)
return context_dict
urlpatterns = [
...
path('accounts/password/change/', MyPasswordChangeView.as_view(
success_url=reverse_lazy('auth_password_change_done')), name='auth_password_change'),
...
]
I have the extra data in services.py but this code gives error:
name 'request' is not defined
So context_dict isn't defined. Where can I take my request from? Mainly I need the user (but print(user)= 'user' is not defined). Or should I write another function?
In methods of Django class based views, you can access the request with self.request.
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context_dict = services.get_base_data_for_views(self.request)
return context_dict
Therefore you can access the user with self.request.user. Normally you would want to use login_required or LoginRequiredMixin so that only logged-in users can access the view, but in your case PasswordChangeView takes care of that for you.
Related
using Django Generic CreateView - is it possible for me to pass a value into the CreateView from the URL call, that defines which model/table to base the view on?
I did try get_context_data but believe that is not the solution, as I think it only pushes it to the rendered template.
You will see from my scripts - I am pushing the value 'syslog_policy' - i would like the view to set the model variable to be, what-ever value I push from the URL.
The reason for this, I have some pages/models that are similar - so rather than create multiple html pages and views - I wouldn't need to if I could get this to work.
URL Call
<li>Update/Delete Policies</li>
urls.py
path('HardenTemplateCreate/<str:element>', HardenTemplateCreate.as_view(success_url="/security_tooling/add_success") ,name="HardenTemplateCreate")
views.py
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['element']= self.kwargs['element']
print(context['element'])
return context
model = !!<NEED VALUE SUPPLIED IN URL HERE>!!
fields = ['name','template']
template_name = 'security_app/add.html'```
This would assume that all the models we are working with here belong to the same app (main) - if not, you also need to pass that to form kwargs and handle accordingly. In forms.py:
from django.apps import apps
from django.forms.models import fields_for_model
class VariableModelForm(forms.Form):
def __init__(self, *args, **kwargs):
model_name = kwargs.pop('model_name', None)
model = apps.get_model(app_label='main', model_name=model_name)
model_fields = fields_for_model(model)
super(VariableForm, self).__init__(*args, **kwargs)
for field in model_fields:
self.fields[field] = model_fields[field]
In your CreateView:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form']= VariableModelForm(model_name=self.kwargs['modelname'])
return context
You grab the model name from the url kwargs, pass it to your form as an extra positional argument, and use it in your form's init method to set up the fields.
I have a class based view which needs to accept a Form submission. I am trying to auto-populate some of the form fields using the primary key within the URL path (e.g. /main/video/play/135). The class based view is based on FormView, the code I have makes the pk available in context if I use TemplateView, but that is not particularly good for handling forms.
urls.py
app_name = 'main'
urlpatterns = [
#path('', views.index, name='index'),
path('video/<int:pk>', views.VideoDetailView.as_view(), name='detail'),
path('video/preview/<int:pk>', views.VideoPreview.as_view(), name='preview'),
path('player', views.PlayerListView.as_view(), name='player_list'),
path('video/play/<int:pk>/', views.VideoPlayView.as_view(), name='play'),
path('', views.VideoListView.as_view(), name="video_list")
]
Relevant class from views.py:
class VideoPlayView(FormView):
template_name = "main/video_play.html"
form_class = VideoPlayForm
initial = {}
http_method_names = ['get', 'post']
def get_initial(self, **kwargs):
initial = super().get_initial()
#initial['video'] = pk
initial['watch_date'] = datetime.date.today()
return initial
def get_context_data(self, **kwargs):
kc = kwargs.copy()
context = super().get_context_data(**kwargs)
video = Video.objects.get(context['pk'])
context['video'] = video
context['test'] = kc
self.initial['video'] = video.pk
context['viewers'] = Viewer.objects.all()
context['players'] = Player.objects.filter(ready=True)
return context
def form_valid(self, form):
return HttpResponse("Done")
I get a key error at the line:
video = Video.objects.get(context['pk'])
Viewing the debug info on the error page indicates that the context does not have the pk value stored within it.
If I change the base class to TemplateView with a FormMixin I don't get this key error (but I do have problems POSTing the form data), so I know that the code is basically okay. My understanding is that the FormView class should populate context in the same way as the TemplateView class.
Any idea why FormView behaves this way, and how can I get this working?
If you want pk from the URL, self.kwargs['pk'] will work in all Django generic class-based-views.
In TemplateView, the get() method passes kwargs to the get_context_data method, so you can use context['pk']. The FormView get() method calls get_context_data() without passing any kwargs, so that won't work.
I want to create one page with both a login and signup form. Per this answer: https://stackoverflow.com/a/1395866/2532070, the best way is to use a separate <form action="{{url}}" tag for each form, and use two different views to handle the POST data for each form. My problem is that using the same view would allow me to pass the errors back to the forms, but if I use separate views with separate urls, I have to redirect the user back to the original page and add these parameters as part of a query string, which doesn't seem very efficient.
My Urls:
url(r'^$', HomePageView.as_view(), name="home_page"),
url(r'^profile/(?P<pk>\d+)/$', MemberUpdate.as_view(template_name="profile.html"), name="profile_page"),
url(r'^signup/$', SignupUser.as_view(), name="signup_user"),
My views:
class HomePageView(TemplateView):
template_name = "index.html"
login_form = AuthenticationForm()
signup_form = UserCreationForm()
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context['login_form'] = self.login_form
context['signup_form'] = self.signup_form
context['signup_action'] = reverse("signup_user")
return context
class SignupUser(CreateView):
model = settings.AUTH_USER_MODEL
form_class = MemberForm
template_name = "index.html"
def get_success_url(self):
return reverse("profile_page", args=[self.object.pk])
def form_invalid(self, form):
# here, how do I pass form.errors back to the home_page?
# I know messages framework is possible but this isn't as
# easy to add each error to its respective field as
# form.errors would be.
return HttpResponseRedirect(reverse('home_page'))
...and I would have a third view for the login form's POST data. Is this an acceptable way to manage my forms/views, or am I better off simply writing one overall view that distinguishes between the signup and login form within its post method with an if statment?
I have a DetailView in django views.py where I want to be able to compare the pk value from the url ex:localhost:8000/myapp/details/3/ and the request.user.id with an if statement.
There is nothing more than the following few lines of code in the view:
class UserDetail(DetailView):
model = Profile
template_name = 'details.html'
Any help would be much appreciated.
Inside a DetailView you have access to self.request, self.args and self.kwargs!
ref.: https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-display/#dynamic-filtering
In your urls.py add something like this:
urlpatterns = [
#...
url(r'^details/(?P<pk>[0-9]+)/$', UserDetail.as_view()),
]
and your UserDetail can now access request.user.id and pk by self.kwargs['pk'] (see reference above: kwargs is name-based, so that you can access it by self.kwargs['name'] and self.args is position-based, so you would access it by self.args[0]).
If I understand your problem correctly, you are trying to manipulate the queryset of the DetailView, to only return the data if the current logged in user is trying to access his page.
If this is true, then you should override get_queryset in your class, like that:
def get_queryset(self):
if self.kwargs['pk'] == self.request.user.id:
return Profile.objects.filter(id=self.request.user.id)
else:
return Profile.objects.none()
I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming