Transform function to class based view (recall same site) - django

Very similar to these questions I want to transform my view. The difference is that I want to return to the same page and I have problems adjusting my urls.py (I think):
So on the product_all.html I press a button and end up on the same page after the product was deleted:
def delete_product(request, pk):
Product.objects.filter(id=pk).delete()
context = {'Product': Product.objects.all()}
return render(request, 'gbkiosk/product_all.html', context)
urls.py:
path("product_delete/<int:pk>", views.delete_product, name='product-delete'),
I wanted to recreate that using a TemplateView:
class DeleteProduct(TemplateView):
template_name = "gbkiosk/device_all.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Product.objects.filter(id=kwargs["product_id"]).delete()
context["products"] = Product.objects.all()
return context
but what would the corresponding urls.py entry be?:
path("product_delete/<int:product_id>", views.DeleteProduct.as_view(), name="product-delete")
This will not return me to product_all.html after clicking?

You should have a ListView to list the products and a DeleteView to delete the ones you like. That's cleaner.
So, it'd be something like:
views.py
class Products(ListView):
model = Product
class ProductDelete(DeleteView):
model = Product
success_url = reverse_lazy('product-list')
urls.py
from django.urls import path
from products.views import ProductListView
urlpatterns = [
path('', ProductListView.as_view(), name='product-list'),
path('<int:pk>', ProductDeleteView.as_view(), name='product-delete'
]

This is how I did it:
class ProductDeleteView(DeleteView):
model = Product
def get_success_url(self):
return reverse_lazy('product-list')
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ProductDeleteView, self).get_context_data(**kwargs)
print(kwargs)
return(context)
urls:
path("product_delete/<int:pk>", views.ProductDeleteView.as_view(), name='product-delete'),

Related

How to redirect using a view name in Django, class-based views?

I have to redirect from one class-based View to another class-based View. I did it by:
return redirect('report')
but the suggestion was to redirect by View names. I did try this, but it doesn't work.
views.py:
class UploadView(View):
def get(self, request, *args, **kwargs):
Reservation.objects.all().delete()
template = "ReportApp/upload.html"
return render(request, template)
def post(self, request, *args, **kwargs):
# try:
csv_file = request.FILES['file']
data_set = csv_file.read().decode('UTF-8')
# setup a stream which is when we loop through each line we are able to handle a data in a stream
io_string = io.StringIO(data_set)
next(io_string)
for column in csv.reader(io_string, delimiter=',', quotechar="|"):
_ = Reservation.objects.update_or_create(
reservation_code=column[0],
checkin=column[1],
checkout=column[2],
flat=column[3],
city=column[4],
net_income=column[5],
)
# return redirect('report')
return redirect(ReportView.as_view())
upload_view = UploadView.as_view()
class ReportView(View):
def get(self, request, *args, **kwargs):
urls.py:
from .views import upload_view, report_view, city_commission_view, error_view
from django.urls import path
urlpatterns = [
path('upload', upload_view, name='upload'),
path('report', report_view, name='report'),
path('city_commission', city_commission_view, name='city_commission'),
path('error', error_view, name='error'),
]
any suggestions how to do this?
Not sure what you want to achieve and I do not see the code for class ReportView(View): def get(self, request, *args, **kwargs):
but perhaps you could try something like this:
from django.views.generic import RedirectView
class ReportView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
url_params = self.kwargs
and play with that.

when a database content deletes ,how can i redirect to same page using url argument passing method?

i want to delete a database content.bt after deletion utl goes to http://127.0.0.1:8004/login/delete_detail/6/ ..how can i redirect to success.html ie in the same page
class DeleteView(generic.TemplateView):
template_name = 'success.html'
success_url='/login/success'
def get_context_data(self, *args, **kwargs):
context = super(DeleteView,self).get_context_data(**kwargs)
did = self.kwargs['did']
q_obj = Quest.objects.filter(id=did)
q_obj.delete()
You should override get_success_url method. For example:
def get_success_url(self):
return reverse_lazy('delete-success')
Also, try to use named urls in your success_url
success_url = reverse_lazy('delete-success')
You can use get_success_url method:
from django.urls import reverse_lazy
class DeleteView(generic.TemplateView):
template_name = 'success.html'
success_url='/login/success'
def get_context_data(self, *args, **kwargs):
context = super(DeleteView,self).get_context_data(**kwargs)
did = self.kwargs['did']
q_obj = Quest.objects.filter(id=did)
q_obj.delete()
def get_success_url(self, **kwargs):
return reverse_lazy('delete_detail', kwargs = {'pk': self.kwargs['did']})
Also instead of TemplateView you can use DeleteView class:
class QuestDelete(DeleteView):
model = Quest
pk_url_kwarg = 'did'
def get_success_url(self, **kwargs):
return reverse_lazy('delete_detail', kwargs = {'pk': self.kwargs['did']})
To use url's name you need to add name argument to the url pattern inside urls.py file like this:
urlpatterns = [
path('delete_detail', views.delete_detail, name='delete_detail'),
]

django Initialising Choices in Form from Class Based View

I've seen a lot of answers about how this is solved when not using Class Based Views. Is there some really obvious thing I'm missing about doing it with CBVs?
Basically I want to have a MultipleChoiceField in my form which has choices determined by what is happening in the view. e.g. I use the PK from the URL to do some backend requests and then those should be used to populate the choices.
# forms.py
from django.forms import Form, MultipleChoiceField, CharField
class EmailForm(Form):
users = MultipleChoiceField(required=False)
subject = CharField(max_length=100)
message = CharField()
def __init__(self, users=None, *args, **kwargs):
super(EmailForm, self).__init__(*args, **kwargs)
if users:
self.fields['users'].choices = users
#urls.py
from django.conf.urls import url, patterns
from .views import EmailView
# url patterns
urlpatterns = patterns('',
url( r'^(?P<pk>\d+)$', EmailView.as_view(), name="maindex" ),
)
#views.py
from django.views.generic import FormView, TemplateView
from .forms import EmailForm
class EmailView(FormView):
template_name = 'myapp/email.html'
form_class = EmailForm
success_ulr = '/thanks/'
def form_valid(self, form):
# Do stuff here
return super(EmailView, self).form_valid(form)
Basically it boils down to how/where to call the init function from the view. How do I do that? Or is there another way I've missed? I thought of overriding get_form_kwargs in the view, but couldn't make that do anything.
Thanks
The view:
from django.views.generic import FormView
class EmailView(FormView):
# ...
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['users'] = users
return kwargs
The form:
from django import forms import Form
class EmailForm(Form):
users = MultipleChoiceField(required=False)
# ...
def __init__(self, *args, **kwargs):
self.users = kwargs.pop('users', None)
super(EmailForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = self.users
Basically, what I've done in a similar case is the following (Python 3.5, Django 1.8):
def get_form(self, *args, **kwargs):
form= super().get_form(*args, **kwargs)
form.fields['rank'].choices= <sequence of 2-tuples>
return form
where obviously rank is the field name. This way I use the default form.
Alright, the FormMixin calls get_form to get the form-class which looks like
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
return form_class(**self.get_form_kwargs())
So you can either override get_form to instance your Form yourself
def get_form(self, form_class):
return EmailForm(files=self.request.FILES or None,
data=self.request.POST or None,
users=some_user_queryset)
or stay a bit more generic and override get_form_kwargs to something like
def get_form_kwargs(self):
form_kws = super(EmailView, self).get_form_kwargs()
form_kws["users"] = some_user_queryset
return form_kws
One way to do it is:
class EmailView(FormView):
# ...
def get(self, request, *args, **kwargs):
self.users = ...
return super(EmailView, self).get(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(EmailView, self).get_form_kwargs()
kwargs['users'] = self.users
return kwargs
This allows you to set the user choices in the view and to pass them to the form.
You can override get_form.
I needed to update the choices for ChoiceField based on logged in user.
Form:
class InteractionCreateForm(forms.Form):
device = forms.ChoiceField(choices=[(None, '----------')])
...
View:
class InteractionCreateView(FormView):
form_class = InteractionCreateForm
...
def get_form(self, form_class=None):
form_class = super().get_form(form_class=None)
form_class.fields['device'].choices = \
form_class.fields['device'].choices \
+ [(device.pk, device) for device in Device.objects.filter(owner=self.request.user.id)]
return form_class

Pass url argument to ListView queryset

models.py
class Lab(Model):
acronym = CharField(max_length=10)
class Message(Model):
lab = ForeignKey(Lab)
urls.py
urlpatterns = patterns('',
url(r'^(?P<lab>\w+)/$', ListView.as_view(
queryset=Message.objects.filter(lab__acronym='')
)),
)
I want to pass the lab keyword argument to the ListView queryset. That means if lab equals to TEST, the resulting queryset will be Message.objects.filter(lab__acronym='TEST').
How can I do that?
You need to write your own view for that and then just override the get_queryset method:
class CustomListView(ListView):
def get_queryset(self):
return Message.objects.filter(lab__acronym=self.kwargs['lab'])
and use CustomListView in urls also.
class CustomListView(ListView):
model = Message
def get(self, request, *args, **kwargs):
# either
self.object_list = self.get_queryset()
self.object_list = self.object_list.filter(lab__acronym=kwargs['lab'])
# or
queryset = Lab.objects.filter(acronym=kwargs['lab'])
if queryset.exists():
self.object_list = self.object_list.filter(lab__acronym=kwargs['lab'])
else:
raise Http404("No lab found for this acronym")
# in both cases
context = self.get_context_data()
return self.render_to_response(context)

Django - CreateView - How to declare variable and use it in templates

How do I declare a variable in Django's Createview, so I can use it from its template?
For example I want to use {{ place_slug }} in the template. I pass that from urls.py like below:
urls.py:
urlpatterns = patterns('',
(r'^new/(?P<place_slug>[\w\-\_]+)/?$', PictureCreateView.as_view(), {}, 'upload-new'),
)
views.py:
class PictureCreateView(CreateView):
model = Picture
def dispatch(self, *args, **kwargs):
self.place = get_object_or_404(Place, slug=kwargs['place_slug'])
return super(PictureCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
more code here
Override get_context_data and set context_data['place_slug'] = your_slug
Something like this:
def get_context_data(self, **kwargs):
context = super(PictureCreateView, self).get_context_data(**kwargs)
context['place_slug'] = self.place.slug
return context
Some more info on this in the Django docs.
in template you can use {{ title }}
class Something(generic.ListView):
template_name = 'app/example.html'
model = models.SomeModel
def get_context_data(self, **kwargs):
context = super(Something, self).get_context_data(**kwargs)
context["title"] = "Some title"
return context