Ive created a Django application where you add jobs and Im fully able to add new jobs but I can update them for some reason? The update feature isn't working and I don't know why??
<form action='/update_job/{{job.id}}' method="POST">
<ul>
{% for message in messages %}
<li>{{message}}</li>
{% endfor %}
</ul>
{% csrf_token %}
Title: <input type='text' value="{{job.job}}" name='job'>
Description: <input type='text' value="{{job.description}}" name='description'>
Location: <input type='text' value="{{job.location}}" name='location'>
<input type='submit'>
</form>
def update_job(request, job_id):
errors = Job.objects.job_validator(request.POST, job_id)
if job.creator.id != request.session['user_id']:
messages.error(request, "This isn't yours to edit!")
if len(errors):
for key, value in errors.items():
messages.error(request, value)
return redirect(f'/edit_job/{job_id}')
job = Job.objects.get(id=job_id)
job.job = request.POST['job']
job.description = request.POST['description']
job.location = request.POST['location']
job.save()
return redirect('/main')
Page not found (404)
Request Method: POST
Request URL: http://localhost:8000/update_job/2
Using the URLconf defined in Python_Exam.urls, Django tried these URL patterns, in this order:
register
login
logout
main
job_link
job/create
jobs/<int:job_id>/view
jobs/<int:job_id>/delete
jobs/<int:job_id>/edit
jobs/<int:job_id>/update_job
The current path, update_job/2, didn't match any of these.
You swapped the primary key and the update_job, it is:
<form action="/jobs/{{ job.id }}/update_job" method="POST">
…
</form>
You however might want to use the {% url … %} template tag [Django-doc] to determine the URL.
Related
I am working on a website using Django. I'm having a difficult time playing around codes, how do I get the ID of a user without passing a primary key (id, pk) in the URL. My code below does not give me a solution to getting the ID of a particular user. When I print (user) it prints the current user logged in. Is there a way to get the ID of other users?
def home(request):
p=Profile.object.filter(user=request.user)
u=p.user
send_request=FriendRequest.objects.filter(from_user=p.user)
def send_request(request, id):
user=get_object_or_404(User, id=id)
frequest=FriendRequest.get_or_creat(from_user=request.user, to_user=user).first()
path('home', home_view, name='home')
<a href='{% url 'site:send_request' profile.user.id %}'>Add friend<a>
Use a form with POST method and send the user_id as a parameter but hidden to the user.
<form method="post" action="{% url 'your_url' %}">
{% csrf_token %}
<input type="hidden" name="user_id" value="{{ user_id }}" />
<button type="submit">Add Friend</button>
</form>
You can access the parameter in the view like this
request.POST.get('user_id')
I want to post to a specific URL. The url has the scope of deleting a database row. The URL is composed by the address + the pk of the file selected in the form catched from a model.
select_file_deletion.html
{% extends "index.html" %}
{% block content %}
<!--Here the number 2 in "/App/delete/2/" needs to be replaced with the pk of the file. The logic is working. -->
<form action="/App/delete/{{ myfile.pk }}/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<h5>Please select one file at a time from the list below to delete it from the server.</h5>
{% for myfile in filename %}
<input type="checkbox" name="file_name" value="{{ myfile }}">
<label>
{{ myfile }}
<input type="hidden" value="{{ myfile.pk }}" name="pk">
</label>
<br>
{% endfor %}
<br>
<button type="submit" class="btn btn-primary">Delete</button>
</form>
{% endblock %}
Project urls.py
url(r'^delete/(?P<pk>\d+)/$', FileDeleteView.as_view(), name='APIdelete')
views.py
class SelectFileDelView(TemplateView):
"""
This view is used to select a file from the list of files in the server.
After the selection, it will send the file to the server.
The server will then delete the file.
"""
template_name = 'select_file_deletion.html'
parser_classes = FormParser
queryset = FileModel.objects.all()
def get_context_data(self, **kwargs):
"""
This function is used to render the list of files in the MEDIA_ROOT in the html template.
"""
context = super().get_context_data(**kwargs)
media_path = settings.MEDIA_ROOT
myfiles = [f for f in listdir(media_path) if isfile(join(media_path, f))]
context['filename'] = myfiles
return context
class FileDeleteView(DeleteView):
"""
This class contains the method to delete a file interacting directly with the API.
DELETE requests are accepted.
"""
# TODO: Fix, still not working
model = FileModel
fields = ['file']
template_name = 'delete_success.html'
success_url = '/delete_success/'
App/urls.py
# Url to select a file to be deleted and confirm the upload
url('filedelete/', SelectFileDelView.as_view(), name='file_delete'),
url('delete_success/', FileDeleteView.as_view(), name='delete_success')
ERROR: the request URL row is not catching the address including the pk, not replacing the variable with the pk of the selected file.
Page not found (404)
Request Method: POST
Request URL: http://127.0.0.1:8000/App/delete//
Using the URLconf defined in DjangoRestDeepLearning.urls, Django tried these URL patterns, in this order:
^App/ ^predict/$ [name='APIpredict']
^App/ ^upload/$ [name='APIupload']
^App/ ^delete/(?P<pk>\d+)/$ [name='APIdelete']
filedelete/ [name='file_delete']
delete_success/ [name='delete_success']
The current path, App/delete//, didn't match any of these.
Question I checked before opening this one without solving the issue:
1) Delete object with form in django
2) Django How to pass object id via form action?
Im assuming you have the myfile.pk in your template.
The url in your form action isnt working as the pk is missing. Replace the action of the form with this:
<form action="{% url 'APIdelete' pk=myfile.pk %}" method="post" enctype="multipart/form-data">
So, Im trying to save a todo to a database with django. My template is as follows:
<form action="{% url 'todo:add' %}" method="POST">
{% csrf_token %}
<input type="text" id="text" value="{{new_todo_text}}"/>
<input type="submit" value="Submit todo" />
</form>
{%if not_done_todos %}
<ul>
{%for todo in not_done_todos%}
<li>
<span>{{todo.text}}</span>
</li>
{%endfor%}
</ul>
{%else%}
<span>No todos for you!</span>
{%endif%}
My view where Im trying to catch the "new_todo_text" looks like this:
def add(request):
"""Add todo to database"""
new_todo = Todo(text=request.POST.get('new_todo_text'),
done=False, date_created=timezone.now())
new_todo.save()
return HttpResponseRedirect(reverse('todo:index'))
The problem is that the todo.text turns up empty no matter what I add to the form... whats my problem here?
The problem is that inside your input element you have not declared a name attribute. That name attibute will be used as a key to fetch it with Django request.POST.
So, change to this:
<input type="text" id="text" name="new_todo_text" value="{{new_todo_text}}"/>
The request.POST dict-like will use the input's name and value to build the dict. Then you can do stuff like request.POST['input_name_here'].
My Goal
I have a django project with a form, and I want to display a preview page before the user submits.
The problem
I can display a preview page using a Django FormPreview, but not all form data is displayed properly. Specifically, if I have a field with choices, the string values of these choices aren't displayed. I'm also having problems applying template filters to date fields. The end result is that some data on the preview page is visible but other data is blank:
However, if I display the same data for posts that have actually been submitted, then everything displays properly:
My Code
models.py:
class Game(models.Model):
# Game Choices
FOOTBALL = 0
BASKETBALL = 1
TENNIS = 2
OTHER = 3
GAME_CHOICES = (
(FOOTBALL, 'Football'),
(BASKETBALL, 'Basketball'),
(TENNIS, 'Tennis'),
(OTHER, 'Other')
)
game_id = models.AutoField(primary_key=True)
location = models.CharField(max_length=200, verbose_name="Location")
game = models.IntegerField(choices=GAME_CHOICES, default=FOOTBALL)
game_date = models.DateField(verbose_name='Game Date')
forms.py
class GameForm(ModelForm):
class Meta:
model = Game
fields = (
'location',
'game',
'game_date'
)
I'm pretty sure that the problem is in my views.py: I'm not sure that I'm processing the POST request the right way to feed all data to the preview page.
views.py
def form_upload(request):
if request.method == 'GET':
form = GameForm()
else:
# A POST request: Handle Form Upload
form = GameForm(request.POST) # Bind data from request.POST into a GameForm
# If data is valid, proceeds to create a new game and redirect the user
if form.is_valid():
game = form.save()
return render(request, 'games/success.html', {})
return render(request, 'games/form_upload.html', {
'form': form,
})
preview.py
class GameFormPreview(FormPreview):
form_template = 'games/form_upload.html'
preview_template = 'games/preview.html'
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/games/success')
form_upload.html
...
<form method="post">
{% csrf_token %}
<ul><li>{{ form.as_p }}</li></ul>
<button type="submit">Preview your post</button>
</form>
...
preview.html
{% load humanize %}
...
<h1>Preview your submission</h1>
<div>
<p>Location: {{ form.data.location }}</p>
<p>Game Date: {{ form.data.game_date|date:"l, F d, Y" }}</p>
<p>Game Type: {{ form.data.get_game_display }}</p>
</div>
<div>
<form action="{% url 'form_upload' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<!-- Submit button -->
<button type="submit">Submit your post</button>
<!-- Go back button -->
<button type="submit">
<a href="{% url 'form_upload' %}"
onClick="history.go(-1);return false;" >
Go back and edit your post
</a>
</button>
</div>
</form>
</div>
...
Two issues
Essentially, I'm having these two issues:
String values for choices are not displayed. If I use the get_FOO_display() method in my preview.html template, it returns blank. However, if I use this in a page after the post has been submitted, it displays properly.
The humanize date filter doesn't work. If I apply a humanize filter ({{ form.data.game_date|date:"l, F d, Y" }}) in preview.html, it also displays blank. Again, this works for submitted posts.
My question essentially is: what's the right way to use the FormPreview here?
form.data does not have get_FOO_display attributes. When you access {{ form.data.get_game_display }} in the template, it fails silently and doesn't display anything.
The get_FOO_display are methods of the instance, so try this instead.
{{ form.instance.get_game_display }}
Wherever possible you should access data from form.cleaned_data (which is validated and 'cleaned') instead of form.data, which is the raw data submitted to the form.
The filters don't work with form.data.game_date because it's a raw string. They should work with form.cleaned_data.game_date, which has been converted to a python date object.
Finally, you haven't implemented anything in your done method, you've just copied the comment from the docs. You could create a new game using cleaned_data as follows:
def done(self, request, cleaned_data):
game = Game.objects.create(**cleaned_data)
return HttpResponseRedirect('/games/success')
I added django.contrib.auth.views.login everywhere in my webpage, for that I had to load a templatetag (that returns the AuthenticationForm) in my base.html. This templatetags includes the registration/login.html template.
The login is working ok but I want it to redirect the users to the same page they are before login. Now, it redirects me to /wherever_i_am/login wich shows registration/login.html with the 'login ok' or 'login fails' messages but without the rest of base.html.
I have followed django documentation and a few SO questions like this but I cannot redirect correctly. I have modified the next variable but it doesn't seem to work (next={{ request.get_full_path }} redirects me to /wherever_i_am/login ...again)
Have you tried something similar? any ideas?
UPDATE1
Now, the question could be something like: Do I have to declare my own login view if I want to include the login form everywhere in my web page?
Thank you.
Found answer:
Change settings.LOGIN_REDIRECT_URL in your settings.py,
below code is copy from django:
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
...
The below allows redirects the user to the page they were attempting to access after they log in, but without having to write a custom view. It contains all the code you need to add to make it work. (As an aside, not all the TEMPLATE_CONTEXT_PROCESSORS are needed, but if you set a value to it explicitly you overwrite the defaults so need to re-add them.)
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
"django.core.context_processors.static",
)
urls.py
from django.contrib.auth.views import login, logout
...the other imports for your app ...
urlpatterns = patterns('',
(r'^login/$', login, {'template_name':'login.html'} ),
(r'^logout/$', logout,{'template_name':'logout.html'}),
...the other urls for your app...
)
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
logout.html
<html>
<p>You are logged out. To log in again, click here.</p>
</html>
views.py
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
I used something like this with default login view:
{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}
<form action="{% url login %}" method="post">
{% csrf_token%}
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
</form>
# or if it's not declareв шт urls:
<form action="{% url django.contrib.auth.views.login %}?next={{ request.get_full_path }}" method="post">
everything worked fine.
PS: are you absolutely sure that "context_processors.request" is included in settings? Forgetting to include it is a common problem.
UPD: As far as I know, there are no way to make default login view to redirect on failed login (It just doesn't work that way).
Still i may be wrong
Finally I created a login view that calls django.contrib.auth.views.login internally.
I'd suggest to pass a previous url as a parameter within the url:
/accounts/login/?next=my_previous_url
and then use this value in a view
request.next
{{request.get_full_path}} gives you the current path, so is normal that the redirect points to the same place, change it for {{next}} in your registration/login.html template
Adding up to #Sean's anwer. Code for iterating over each form field in order to write field error above the miss-typed field.
So, in Sean's login.html is the existing code:
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/> <!-- I can change! -->
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
Now what you should do is replace the "I can change!" line (4th line in the above code snippet) with following code:
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small"> {{ field.errors }}</span>
</div>
<label class="control-label col-sm-2">{{ field.label_tag }}</label>
<div class="col-sm-10"> {{ field }}</div>
</div>
{% endfor %}
You can use this snippet for other forms too (for example registration). :)
I stumble upon this question in my process of implementing Facebook Account Linking. The problem is the same: how do I correctly redirect django after successful login?
Remember this: your settings.py contain LOGIN_REDIRECT_URL right? So, that's the only place where you should do the logic of redirecting. To do that, first connect this signal (put this in your views.py):
def after_success_login(sender, user, request, **kwargs):
alt = request.GET.get('account_linking_token')
if alt is not None:
uri = request.GET.get('redirect_uri')
request.session['fb_redirect_uri'] = uri
user_logged_in.connect(after_success_login)
The logic above may not reflect your case, but the idea is setting up a session variable to be read in the route defined as LOGIN_REDIRECT_URL.
So, in my case:
def index(request):
if not request.user.is_authenticated():
form = SignUpForm()
return render(request, 'index.html', {'form': form})
else:
# FB ACCOUNT LINKING!
if 'fb_redirect_uri' in request.session:
redirect_uri = request.session['fb_redirect_uri']
del request.session['fb_redirect_uri']
to = '{}&authorization_code={}'.format(redirect_uri, request.user.username)
print('to', to)
return redirect(to)
That's it!
Add a decorator before the view function should be OK.
#login_required
see here for details