So currently I'm on a page with a url link like this
urls.py :
path('key/<int:pk>', views.KeyDetailView.as_view(), name='roomkey-detail'),
views.py :
class KeyDetailView(generic.DetailView):
model = RoomKey
this lists out a list of keys available to be borrowed for a particular room. Then when I try to head to the next page, where is a request I can make to borrow out one of the keys for that room, Here is the urls and views responsible for rendering the roomkey-request page
urls.py :
path('key/<int:pk>/request', views.KeyRequestCreate.as_view(), name='roomkey-request')
views.py :
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['roomkey', 'requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
there is a button that on that page that links to a terms and agreement page that looks like this
roomkey_request_form.html :
terms and conditions
urls.py :
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),
views.py :
def KeyAgreement(request):
return render(
request,
'catalog/roomkey_agreement.html',
)
however when try to click on that request button to request a key, django throws an error
NoReverseMatch at /catalog/key/2/request
Reverse for 'key-agreement' with arguments '('',)' not found. 1 pattern(s)
tried: ['catalog\\/key\\/(?P<pk>[0-9]+)\\/request\\/agreement$']
I have a button on the terms and agreement to go back to the request page something that looks like this
<button href="{% url 'roomkey-request' roomkey.pk %}" >Return to request</button>
will this return to the request page with the correct pk? I think i'm just confused with how the url handles pk and how it get's passed on.I am thinking this had to do with something the keyagreement not being able to take in that pk from the details page, can someone please explain to me what I am doing wrong or point me to some resource that can help me understand how urls pass along the pk from view to view? I am fairly new to django so thanks for your help in advance!!
Try:
def KeyAgreement(request, pk): #-->pk in argument
return render(
request,
'catalog/roomkey_agreement.html',
)
If you want to use roomkey.pk in the roomkey_request_form.html template, you need to add roomkey to the template context. You can do this in the get_context_data method.
Since you already have the roomkey pk from the URL, you can remove it from fields. Then set roomkey in the form_valid method before saving.
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
def get_context_data(self, **kwargs):
context = super(KeyRequestCreate, self).get_context_data(**kwargs)
context['roomkey'] = get_object_or_404(RoomKey, pk=pk)
def form_valid(self, form):
form.instance.roomkey = get_object_or_404(RoomKey, pk=pk)
return super(KeyRequestCreate, self).get_form(form)
If you want to use roomkey in the agreement view, you'll have to make some changes to it as well.
First, you need to add pk to the function signature since you have <int:pk> in its URL pattern.
Then, you need to include roomkey in the template context.
from django.shortcuts import get_object_or_404
def key_agreement(request, pk):
roomkey = get_object_or_404(roomkey, pk=pk)
return render(
request,
'catalog/roomkey_agreement.html',
{'roomkey': roomkey}
)
Note that I've renamed the view function to key_agreement to match the recommended style. You'll need to update the URL pattern as well.
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),
Related
Sorry if the title is unclear, I'm not sure the best way to describe the issue. I have an application with a Ticket model and a Team model. All Tickets are associated with a single Team. The issue I'm having is a problem of URL reversing. I'm trying to set it up my URLs like so: /<team_pk>/tickets/ displays a list of tickets associated with the Team specified by team_pk. So /1/tickets/ would display all of the tickets for the first Team. Both of these objects are in the app tracker.
To do this, I've set up my project/urls.py files like so:
project/urls.py
urlpatterns = [ path('<team_pk>/', include('tracker.urls', namespace='tracker')), ]
tracker/urls.py
urlpatterns = [ path('tickets/', views.TicketTable.as_view(), name='ticket_list'), ]
Then, inside my html templates, I have the following URL tag:
href="{% url 'tracker:ticket_list' %}"
This results in a NoReverseMatch error:
NoReverseMatch at /1/tickets/
Reverse for 'ticket_list' with no arguments not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/tickets/$']
What I would like is for the reverse match to just use the current value for the team_pk URL kwarg.
What I have tried to fix it:
I have found the following solution to the problem, but it involves a lot of repetition, and I feel like there must be a DRYer way.
First, I extend the get_context_data() method for every view on the site.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['current_team_pk'] = self.kwargs['team_pk']
return context
Then I reference this context in every URL template tag:
href="{% url 'tracker:ticket_list' team_pk=current_team_pk %}"
This results in the desired behavior, but it's a lot of repetition. So, is there a better way?
Edit:
Based on Willem Van Onsem's suggestion, I changed the URL template tag to href="{% url 'tracker:ticket_list' team_pk=team_pk %}", referencing the URL kwarg directly. But this doesn't seem to be working reliably. On the index page /<team_pk>/ loads just fine, and it includes two relevant URLs: /<team_pk>/ and /<team_pk>/tickets/. When I navigate to /<team_pk>/tickets/, however, I get another NoReverseMatch error:
NoReverseMatch at /1/tickets/
Reverse for 'home' with keyword arguments '{'team_pk': ''}' not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/$']
It seems the URL kwarg <team_pk> is not being passed for some reason. But the only link to 'home' is part of my base.html, which the other templates are extending. So the relevant template tags are the same.
Edit 2:
The view in question:
class TicketTable(LoginRequiredMixin, SingleTableMixin, FilterView):
table_class = my_tables.TicketTable
template_name = 'tracker/ticket_list.html'
filterset_class = TicketFilter
context_object_name = 'page'
table_pagination = {"per_page": 10}
PAGE_TITLE = 'Open Tickets'
TICKET_STATUS_TOGGLE = 'Show Closed Tickets'
TICKET_STATUS_TOGGLE_URL = 'tracker:closed_ticket_list'
DISPLAY_DEV_FILTER = True
DISPLAY_STATUS_FILTER = True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_title'] = self.PAGE_TITLE
context['ticket_status_toggle'] = self.TICKET_STATUS_TOGGLE
context['ticket_status_toggle_url'] = self.TICKET_STATUS_TOGGLE_URL
context['display_dev_filter'] = self.DISPLAY_DEV_FILTER
context['display_status_filter'] = self.DISPLAY_STATUS_FILTER
return context
def get_queryset(self):
return models.Ticket.objects.filter_for_team(self.kwargs['team_pk']).filter_for_user(self.request.user).exclude(status='closed')
Edit 3:
I found a solution to access the URL kwargs without modifying context data. I'm not sure why team_pk=team_pk didn't work in the URL tag, but team_pk=view.kwargs.team_pk does work.
Based on the comments and responses from Willem Van Onsem, a friend of mine, and some of my own digging, I found what I think is the DRYest way to do what I was trying to do. I created a custom mixin:
class CommonTemplateContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.kwargs.get('team_pk'):
context.setdefault('team_pk', self.kwargs.get('team_pk'))
else:
context.setdefault('team_pk', self.request.user.teams.all()[0].pk)
return context
Then I subclass this mixin with all my views. Then I have a team_pk template tag inside all my templates, and I just add team_pk=team_pk to all my URL template tags. The only advantage to using this instead of team_pk=view.kwargs.team_pk is that I can add some additional logic for the case when the URL kwarg isn't available.
I have a django CreateView where users can create new words. I also have an unrelated Song model, where users can choose to add words from the lyrics of the songs. So I've created a separate CreateView for this, so that I can have a different success url. The success url should go back to the song where the user was browsing. But I am struggling to figure out how to pass the pk of this particular object to the CreateView of a different model.
This is my special CreateView:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView):
template_name = 'vocab/add_custom_initial.html'
fields = ("target_word","source_word", etc.)
model = models.Word
from videos.models import Song
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(CreateWordFromSong, self).form_valid(form)
success_url = reverse_lazy('videos:song-vocab', kwargs={'pk': song_pk) #how to get song_pk?
Everything works when I replace song_pk with the actual pk.
I overwrite form_valid so that the user can be saved with the new object. Perhaps there is a way I can alter this so that I can also get the song_pk? I've played around with it a bit, but without luck.
I get the song_pk from my url:
path('song/<int:song_pk>/', views.CreateWordFromSong.as_view(), name='create-song'),
So I should have access to it. But when I try adding it to my view:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView, song_pk)
I get the error: name 'song_pk' is not defined.
You could add in class CreateWordFromSong instead success_url method get_success_url
def get_success_url(self):
return reverse_lazy('videos:song-vocab', kwargs={'pk': self.kwargs.get('song_pk')})
It will be work, if action in template with form be like this
<form action="{% url 'create-song' song_pk=song.pk %}" method="post">
(Surely a bit lately ...)
I faced a similar case but not sur this matches with your.
I have a list of Mapping objects (children) related to Flow object (parent and ForeignKey).
This list uses the Flow PK.
urls.py
path('flow/<int:pk>/mapping',
mappingColumnOneFlowListView.as_view(), name='mapping-details'),
On each line of Mapping objects, I got a "delete" button.
After deletion, I want to lead back to the list of the Flow Mapping but couldn't because the PK used in the view lead to the (deleted) Mapping.
Another solution would have consisted in leading to another URL without pk (eg. absolute_url in Model) but this was not what I wanted.
It's surely not the best way to achieve the goal but I found this:
in my DeleteView i added :
def get_success_url(self):
pk = self.kwargs['pk'] # Mapping's PK
flow_pk = MappingField.objects.filter(
pk=pk).first().fl_id.pk # fl_id.pk = Flow's PK (FK)
return reverse('mapping-details', kwargs={'pk': flow_pk})
Not sure if I should be doing this or not... but here goes.
I have a url that looks like this:
/deals/category/Apparel/
I changed it to category/Apparel to shorten it.
but also notice the capitalized Apparel --as it is using the category name.
So I added a slug to my Category model and I'm trying to redirect the
deals/category/Apparel to category/apparel where the latter represents the slug
In my deals app I have this URL:
path('category/<str:category>', RedirectView.as_view(pattern_name='category', permanent=True)),
and which I'm trying to redirect to (in my core urls file)
path('category/<slug:slug>', deals_by_category, name='category')
My view for the `deals_by_category' looks like this:
def deals_by_category(request,slug):
category_deals = Deal.objects.filter(category__slug=slug).order_by('expired','-date_added')
category = category_deals[0].category
return render(request, 'deals/category.html', {'category_deals': category_deals, 'category':category})
so when I go to deals/category/Apparel it is redirecting to category/Apparel which is not what I want... and I get an error like this:
Reverse for 'category' with keyword arguments '{'category': 'Apparel'}' not found. 1 pattern(s) tried: ['category\\/(?P<slug>[-a-zA-Z0-9_]+)$']
I guess I understand that it is looking at the category name and trying to match against a slug, but not exactly sure how to correctly redirect this with the correct format.
path('category/<str:category>', RedirectView.as_view(pattern_name='category', permanent=True)),
When you use pattern_name, Django will try to reverse the URL with the same args and kwargs, in this case category='Apparel'.
If you want to use the slug in the URL instead, then you'll have to subclass RedirectView and override get_redirect_url.
from django.shortcuts import get_object_or_404
class CategoryRedirectView(RedirectView):
permanent = True
def get_redirect_url(self, *args, **kwargs):
category = get_object_or_404(Category, name=self.kwargs['category'])
return reverse('category', kwargs={'slug': category.slug})
Then use your view in your URL pattern:
path('category/<slug:slug>', CategoryRedirectView.as_view(), name='category')
I wouldn't set permanent = True until you are sure that the redirect is working as expected. Otherwise browsers may cache incorrect redirects.
I want that the landing page of my homepage is a form with an input and the user puts in stuff. So I followed a couple of tutorials and now I have this:
views.py:
def create2(request):
if request.method =='POST':
form = LocationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('')
else:
form = LocationForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('location/index.html', args)
and in my urls.py:
url(r'^$', 'core.views.create2'),
which works perfectly fine, if I go to 127.0.0.1:8000 I get to index.html and when put in something in the input it gets saved in the database. However, the old part of my homepage looks like this
class LandingView(TemplateView):
model = Location
template_name="location/index.html"
def search
...
and the urls.py:
url(r'^$', core.views.LandingView.as_view(), name='index'),
which has a function search I So my question is, is there a way how I can merge the def create2 into my LandingView. I tried several things, but I am always ending up having the index.html without the input field. I also tried
def create2
...
def search
...
but didn't work.
Does anyone know how to merge that together?
EDIT
Thank you the working solution looks like this now
class Create(CreateView):
model = coremodels.Location
template_name = 'location/test.html'
fields = ['title']
def form_valid(self, form):
form.save()
return HttpResponseRedirect('')
return super(Create, self).form_valid(form)
Depending on the results you are looking for, there are multiple ways to solve this:
1. Use CreateView and UpdateView
Django already provides some classes that render a form for your model, submit it using POST, and re-render the form with errors if form validation was not successful.
Check the generic editing views documentation.
2. Override get_context_data
In LandingView, override TemplateView's get_context_data method, so that your context includes the form you are creating in create2.
3. Use FormView
If you still want to use your own defined form instead of the model form that CreateView and UpdateView generate for you, you can use FormView, which is pretty much the same as TemplateView except it also handles your form submission/errors automatically.
In any case, you can keep your search function inside the class-based view and call it from get_context_data to include its results in the template's context.
I am new to django. I made a form. I want that if the form is filled successfully then django should redirect to a success page showing the name entered in the form but no parameters should be present in the url itself.
I searched on the internet and the solution I got was to redirect to url with pk as a get parameter which fetches the data and shows in the view. But I don't want to pass any thing in the url itself. and some websites say that http can't redirect with post data.
Here's my views.py
class UserRegistrationView(CreateView):
model = UserForm
template_name = 'userregistration.html'
form_class = UserForm
success_url = 'success'
def get_success_url(self):
return reverse('success',kwargs = {'name' : self.object.firstName})
and here's the template to which I want to redirect:
<h2>Congratualations for registering {{name}} </h2>
Basically what I want is that if the person fill form mentioning his/her firstName as "xyz" then the redirected success page should say that "Congratulations for registering xyz"
You can use django sessions, which I believe installed by default in 1.8
Look here
# Set a session value:
request.session["fav_color"] = "blue"
# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session["fav_color"]
# Clear an item from the session:
del request.session["fav_color"]
You can pass your pk via session and extract your object in another view without affecting your url.
Make sure you clean up after yourself.
Let me know if more help needed.
One of the possible ways of passing data between views is via sessions. So, in your UserRegistrationView you need to override the form_valid method as shown below.
class UserRegsitrationView(CreateView):
def form_valid(self,form):
self.request.session['name'] = self.object.firstName
return super(UserRegistrationView,self).form_valid(form)
class SuccessView(TemplateView):
template_name = "success_template.html"
def get_context_data(self,**kwargs):
context = super(SuccessView,self).get_context_data(**kwargs)
context['name'] = self.request.session.get('name')
del self.request.session['name']
return context
One more thing that you can modify in your code is that you need not declare success_url if you are overriding get_success_url