1
I'm working on Django. I'm getting the error below. I didn't find the solution despite the much increased.Please refer the this link for trace back
Codes in views.py
class UpdateVote(LoginRequiredMixin,UpdateView):
form_class = VoteForm
queryset = Vote.objects.all()
def get_object(self,queryset=None):
vote = super().get_object(queryset)
user = self.request.user
if vote.user != user:
raise PermissionDenied('can not change another user vote')
return vote
def get_success_url(self):
movie_id = self.object.movie.id
return reverse('core:movie_detail', kwargs={'pk':movie_id})
def render_to_response(self, context, **response_kwargs):
movie_id = context['object'].id
movie_detail_url = reverse('core:movie_detail',kwargs={'pk':movie_id})
return redirect(to=movie_detail_url)
class MovieDetail(DetailView):
queryset = Movie.objects.all_with_prefetch_persons()
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
vote = Vote.objects.get_vote_or_unsaved_blank_vote(movie=self.object,user=self.request.user)
if vote.id:
vote_url_form = reverse('core:UpdateVote',kwargs={'movie_id':vote.movie.id,'pk':vote.id})
else:
vote_url_form = (reverse('core:create_vote',kwargs={'movie_id':self.object.id}))
vote_form = VoteForm(instance=vote)
ctx['vote_form'] = vote_form
ctx['vote_url_form'] = vote_url_form
return ctx
Codes in form.py
I have used this form to link with UpdateView
from django import forms
from django.contrib.auth import get_user_model
from .models import Movie,Vote
class VoteForm(forms.ModelForm):
user = forms.ModelChoiceField(widget=forms.HiddenInput,queryset=get_user_model().objects.all(),disabled=True)
movie = forms.ModelChoiceField(widget=forms.HiddenInput,queryset = Movie.objects.all(),disabled=True)
value = forms.ChoiceField(widget=forms.RadioSelect,choices=Vote.VALUE_CHOICE)
class Meta:
model = Vote
fields = ('value','user','movie',)
urls.py
This is the url mapping for the view.
from django.contrib import admin
from django.urls import path
from .views import MovieList,MovieDetail,PersonDetail,CreateVote,UpdateVote
app_name = 'core'
urlpatterns = [
path('movies/', MovieList.as_view(), name='movie_list'),
path('movie/<int:pk>/', MovieDetail.as_view(), name='movie_details'),
path('person/<int:pk>/', PersonDetail.as_view(), name='person_details'),
path('movie/<int:movie_id>/vote/', CreateVote.as_view(), name='create_vote'),
path('movie/<int:movie_id>/vote/<int:pk>', UpdateVote.as_view(), name='UpdateVote'),
]
HTML template
This is the template I used.
{% block sidebar %}
<div>
{% if vote_form %}
<form action="{{vote_form_url}}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ vote_form.as_p }}
<button class="btn btn-primary" type="submit" >Vote</button>
</form>
{% else %}
<p>Login to vote for this movie</p>
{% endif %} </div> {% endblock %}
Your UpdateVote view is using VoteForm and the queryset on that view is from Vote model, so that the object field inside that view is the instance of Vote model, not Movie model.
This code movie_id = context['object'].id also not work because context might not included object of UpdateVote view, that caused the error KeyError, Exception Value: 'object'. You could get movie_id via kwargs field inside UpdateVote view because you've already defined movie_id in the path.
With this:
path('movie/<int:movie_id>/vote/<int:pk>', UpdateVote.as_view(), name='UpdateVote'),
Your view can get the values by using kwargs like so:
class UpdateVote(LoginRequiredMixin,UpdateView):
form_class = VoteForm
queryset = Vote.objects.all()
def get_object(self,queryset=None):
vote = super().get_object(queryset)
user = self.request.user
if vote.user != user:
raise PermissionDenied('can not change another user vote')
return vote
def get_success_url(self):
movie_id = self.kwargs.get('movie_id')
return reverse('core:movie_detail', kwargs={'pk':movie_id})
def render_to_response(self, context, **response_kwargs):
movie_id = self.kwargs.get('movie_id')
movie_detail_url = reverse('core:movie_detail',kwargs={'pk':movie_id})
return redirect(to=movie_detail_url)
Related
I don't know what type of data should I use there. With primary key I think is there are no problem. But it's not that what I needed.
This is part of my models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils.timezone import now
class Parcella(models.Model):
###
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.parcellanev
class Muvelet(models.Model):
###
parcella = models.ForeignKey(Parcella, on_delete=models.CASCADE)
This is part of my views.py:
#login_required
def muvelethozzaadas(request, parcella_pk):
if request.method == 'GET':
return render(request, 'foldmuv/muvelethozzaadas.html', {'form':MuveletForm()})
else:
try:
form = MuveletForm(request.POST)
ujmuvelet = form.save(commit=False)
ujmuvelet.parcella = parcella_pk
ujmuvelet.save()
return redirect('parcellak')
except ValueError:
return render(request, 'foldmuv/muvelethozzaadas.html', {'form':MuveletForm(), 'error':'Nem megfelelő adat. Kérlek prbáld újra!'})
This is part of my parcellaegy.html:
<form method="POST" action="{% url 'muvelethozzaadas' parcella.id %}">
{% csrf_token %}
<button type="submit">Hozzáadás</button>
</form>
First You have to get object of Parcella using parcella_pk and assign that object to ujmuvelet.parcella as...
parcella_obj = Parcella.objects.get(id=parcella_pk)
ujmuvelet.parcella = parcella_obj
ujmuvelet.save()
#UPDATE
I want to make dynamic multiple buttons that filtering objects, similar to website https://justjoin.it/brands
For now I've done filtering with Django-filter that allows users to filter companies by Type, City and is company open for Students. But this django-filter require page to be refreshed which means it's not dynamic updating. It looks like this
image 1
image 2
and this is functional, works excellent.
Then I've created simple API with django-rest-framework which is accesible on 127.0.0.1:8000/api/companies and looks like this
Image
and also works fine.
The next thing that I've made is adding jQuery and simple script to get data from API, when someone submit form with ID=submit (image below)
image
But now I've stucked because I dont know how to get Data from API and compare this to template. I'm asking for any suggetions because I couldnt find any good example.
My files (updated)
models.py
from django.db import models
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
from django.core.validators import MinValueValidator
from multiselectfield import MultiSelectField
import django_filters
TYPES = (
('Startup', 'Startup'),
('Software House', 'Software House'),
....)
CITIES = (
('Warszawa', 'Warszawa'),
('Poznan', 'Poznan'),
....)
COMPANY_TECHNOLOGIES = (
('PHP', 'PHP'),
('js', 'JavaScript'),
....)
STUDENTS = (
('No', 'No'),
('Yes', 'Yes')
)
class Company(models.Model):
name = models.CharField(max_length=100, blank=False)
students = models.CharField(max_length=3, choices=STUDENTS)
type = models.CharField(max_length=15, choices=TYPES)
workers = models.PositiveIntegerField(validators=[MinValueValidator(1)])
city = models.CharField(max_length=15,choices=CITIES)
stack = MultiSelectField(choices=COMPANY_TECHNOLOGIES)
....
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Company, self).save(*args, **kwargs)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Company
from .filters import CompanyFilter
from rest_framework import viewsets
from .serializers import CompanySerializer
# Create your views here.
def companies_list(request):
my_companies = Company.objects.all()
## filter by company type
type = request.GET.get('type')
if type:
my_companies = Company.objects.filter(type=type)
## filter by company city
city = Company.objects.all()
if city:
my_companies = Company.objects.filter(city=city)
## filter by company technologies
stack = request.GET.get('stack')
if stack:
my_companies = Company.objects.filter(city=city)
my_companies = my_companies.order_by('published_date')
return render(request, 'company/companies_list.html', {'my_companies': my_companies})
def comp_list(request):
f = CompanyFilter(request.GET, queryset=Company.objects.all())
return render(request, 'company/comp_list.html', {'filter': f})
##def brands(request, slug):
## brands = Company.objects.all()
##return render(request, 'company/comp_view.html', {'brands': brands})
def brands(request, pk):
brand = get_object_or_404(Company, pk=pk)
return render(request, 'company/comp_view.html', {'brand': brand})
comp_list.html
{% extends 'company/base.html' %}
{% block content %}
<div id="filter">
<form action="" method="get" id="submit">
{{ filter.form.as_p }}
<input type="submit"/>
</form>
{% for obj in filter.qs %}
{{ obj.name }}
<p>Image {% if obj.image != None %}
<img src="{{ obj.image.url }}">
{% endif%}</p>
<p>Icon {% if obj.icon != None %}
<img src="{{ obj.icon.url }}" width="30" height="30">
{% endif%}</p>
<br> Type: {{ obj.type }} City: {{ obj.city }} Stack: {{ obj.stack }}
<br />
<br>
{% endfor %}
{% endblock %}
serializers.py
from .models import Company
from rest_framework import serializers
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = "__all__"
filters.py
import django_filters
from .models import Company, COMPANY_TECHNOLOGIES
from django_filters import ChoiceFilter
class CompanyFilter(django_filters.FilterSet):
class Meta:
model = Company
fields = ['type', 'city', 'students']
def __init__(self, *args, **kwargs):
super(CompanyFilter, self).__init__(*args, **kwargs)
self.filters['type'].extra.update(
{'empty_label': 'All'})
self.filters['city'].extra.update(
{'empty_label': 'All'})
self.filters['students'].extra.update(
{'empty_label': 'All'})
ajax.js
$( "#submit" ).click(function(event) {
event.preventDefault();
$.ajax({
url: "http://127.0.0.1:8000/api/companies/",
method: 'GET',
success: function(data){
console.log(data)
},
error: function(error_data){
console.log("error")
console.log(error_data)
}
})});
Filtering is usually done via GET parameters.
In this example, if you add ?company_name=acme to the URL, only company names with acme in the name would be shown:
def companies_list(request):
my_companies = company.objects.filter(
published_date__lte=timezone.now())
company_name = request.GET.get('company_name', None)
if company_name:
my_companies = my_companies.filter(company_name__icontains=company_name)
my_companies = my_companies.order_by('published_date')
return render(request, 'company/index.html', {'my_companies': my_companies})
There's also django-filter to ease this stuff.
As for the SPA, this is beyond the scope of the question, but it should be done with an AJAX request and replacing the DOM element with the response of the view, or returning a JSONResponse and building the element with JavaScript.
I'm trying to write a DeleteView for deleting posts without getting displayed a confirmation page.
Del - delete button. How can I delete the object immediately?
urls.py:
urlpatterns = [
# url(r'^$', views.index, name='index'),
url(
r'^feed$',
views.FeedView.as_view(),
name='feed'
),
url(r'^summary(?P<pk>\w{0,50})',
views.SummaryCreate.as_view(),
name='summary'),
url(r'^summary(?P<user_id>\w{0,50})/(?P<pk>\w{0,50})/',
views.SummaryDelete.as_view(),
name='delete_summary'),
url(r'^dashboard$',
permission_required('reed.view_dashboard')
(views.DashboardListView.as_view()),
name='dashboard'),
url(r'^dashboard/(?P<pk>\w{0,50})',
permission_required('reed.view_dashboard')
(views.DashboardUpdate.as_view()),
name='review_summary'),
]
views.py
class SummaryCreate(LoginRequiredMixin, generic.CreateView):
template_name = 'template/summary_list.html'
model = Summary
form_class = AddUrlForm
login_url = '/login_page/login/'
redirect_field_name = 'login_page'
def get_context_data(self, **kwargs):
return dict(
super(SummaryCreate, self).get_context_data(**kwargs),
summary_list=reversed(Summary.objects.filter(user_id=self.kwargs['pk']).reverse())
)
def get_success_url(self):
return reverse('summary', args=(self.request.user.id.hex,))
def form_valid(self, form):
print(self.request.user.id.hex)
url_inst = form.save(commit=False)
keywords_inst = Keywords
article = Article(form.cleaned_data['url'], language='en')
article.download()
article.parse()
title = article.title
print(title)
try:
image = article.top_image
print(image)
except Exception:
image = ''
article.nlp()
try:
keywords = article.keywords
print(keywords)
except Exception:
keywords = 'Sorry,no,keywords,found'
try:
summary = article.summary
print(summary)
except Exception:
summary = 'Sorry, no summmary found'
try:
publish_date = article.publish_date
publish_date = publish_date.date()
print(publish_date)
except Exception:
publish_date = '1900-01-01'
user = User.objects.get(id=self.request.user.id.hex)
url_inst.url=form.cleaned_data['url']
url_inst.image=image
url_inst.title=title
url_inst.summary=summary
url_inst.date=publish_date
url_inst.user_id=user
url_inst.save()
summary = Summary.objects.get(url=form.cleaned_data['url'])
#
for keyword in keywords:
new_keyword = keywords_inst(keyword=keyword, keyword_id=summary)
new_keyword.save()
#
return super(SummaryCreate, self).form_valid(form)
class SummaryDelete(SummaryCreate, generic.DeleteView):
model = Summary
pk_url_kwarg = 'pk'
slug_url_kwarg = 'pk'
def get_success_url(self):
return reverse('summary', args=(self.request.user.id.hex,))
def dispatch(self, request, *args, **kwargs):
return super(SummaryDelete, self).dispatch(request, *args, **kwargs)
template.html:
<form action="{% url 'delete_summary' user.id.hex summary.id.hex %}" method="post">{% csrf_token %}
<h3>
<input type="submit" class="delete" aria-hidden="true" value="X">
{{summary.title}}
</h3>
</form>
I have 2 classes in one template: 1 for displaying all posts and adding new posts and second for deleting, but deleting only redirect me on page, that I provide for DeleteView.
DeleteView:
A view that displays a confirmation page and deletes an existing
object. The given object will only be deleted if the request method is
POST. If this view is fetched via GET, it will display a confirmation
page that should contain a form that POSTs to the same URL.
You need a form element in order to send a POST request.
template.html:
<form id="my_form" method="post" action="{% url 'delete_summary' user.id.hex summary.id.hex %}">
{% csrf_token %}
</form>
Del
By adding the following code in one of my models, I was able to add a view permission to the model.
class Meta:
default_permissions = ('add', 'change', 'delete', 'view')
I have created a user in django-admin and given only view permission. I want that the user should be able to only view the values of this particular model.
But after logging in with the user I am getting an error that the user dont have the permission to edit anything.(refer screenshot).
Is there any way that I can allow the user to view a particular model only by giving the VIEW permission only? Any help would be greatly appreciated.
It involves quite a hassle. I'll dump few ideas below.
First, let's show a model with a view prermission in the admin:
from django.contrib import admin
from django.contrib.auth import get_permission_codename
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def get_model_perms(self, request):
model_perms = super(MyModelAdmin, self).get_model_perms(request)
view_perm = self.has_view_permission(request)
model_perms.update({
'change': view_perm,
'view': view_perm,
})
def has_view_permission(self, request):
opts = self.opts
codename = get_permission_codename('view', opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
Note: I'm hooking in the changelist_view(). I find it easier.
Secondly, let's add two views viewlist_view() and view_view() to the MyModelAdmin:
def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
wrapper.model_admin = self
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(MyModelAdmin, self).get_urls()
urls = [
url(r'^viewlist/$', wrap(self.viewlist_view), name='%s_%s_viewlist' % info),
url(r'^(.+)/view/$', wrap(self.view_view), name='%s_%s_view' % info),
]
return urls + urlpatterns
#csrf_protect_m
def viewlist_view(self, request, extra_context=None):
opts = self.model._meta
app_label = opts.app_label
if not self.has_view_permission(request):
raise PermissionDenied
context = dict(
self.admin_site.each_context(request),
title='%s view list' % force_text(opts.verbose_name),
queryset=self.get_queryset(request),
)
context.update(extra_context or {})
return TemplateResponse(request, 'admin/view_list.html', context)
#csrf_protect_m
def view_view(self, request, object_id, extra_context=None):
opts = self.model._meta
app_label = opts.app_label
if not self.has_view_permission(request):
raise PermissionDenied
context = dict(
self.admin_site.each_context(request),
object_id=object_id,
)
return TemplateResponse(request, 'admin/view_view.html', context)
Thirdly, hook in the changelist_view():
#csrf_protect_m
def changelist_view(self, request, extra_context=None):
if not self.has_change_permission(request, None):
# or redirect
return self.viewlist_view(request, extra_context)
return super(MyModelAdmin, self).changelist_view(request, extra_context)
Fourthly, add two templates:
admin/view_list.html
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content-main">
<ul>
{% for obj in queryset %}
<li>{{ obj }}</li>
{% endfor %}
</ul>
</div>
{% endblock %}
admin/view_view.html
{% extends "admin/base_site.html" %}
{% block content %}
<div id="content-main">
{{ object_id }}
</div>
{% endblock %}
Lastly, I've added a method get_admin_view_url() to the MyModel to get to the admin view:
from __future__ import unicode_literals
from django.db import models
from django.core.urlresolvers import reverse
class MyModel(models.Model):
name = models.CharField(max_length=255)
class Meta:
default_permissions = ('add', 'change', 'delete', 'view')
def __unicode__(self):
return self.name
def get_admin_view_url(self):
info = (self._meta.app_label, self._meta.model_name)
return reverse('admin:%s_%s_view' % info, args=(self.id,))
I hope this makes sense.
I'm trying to add a new feature to my existing app that let users create a profile and upload a pictures of their pets.
When a user login , he gets redirected into the profile which display his name and also he can add a picture of himself into the model which will get displayed on the profile page.
At the moment , I can retrieve the name into the template but I can't seem to display the user's name and upload picture at the same time.
Whenever I click Add picture , It doesn't let the user upload a picture instead I get this error
'PictureForm' object has no attribute 'save'
pet = form.save(commit =False) ...
I could design the page to let the user upload a picture but not display the name at the same time.
I think the problem lays in my profile.html and Profile function at views.py
Parts of my views.py
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILE or None)
if form.is_valid():
pet = form.save(commit =False)
pet.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
Parts of my forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from pet.models import *
class PictureForm(forms.Form):
class Meta:
model = Person
fields = ('image')
My profile.html
{% if Person %}
<ul>
<li>Name : {{Person.name}} </li>
</ul>
{% endif %}
<form method="POST" enctype="multipart/form-data" "action" >
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type = "submit" value= "Add Picture" />
</form>
My models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Person(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.name
class Pet(models.Model):
Person = models.ForeignKey(Person)
description = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.description
PictureForm needs to inherit from forms.ModelForm, not forms.Form.
Erase your form.save(commit=False). You will only do that if you override your save method
#login_required
def Profile(request):
Person = request.user.get_profile()
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
if request.method == "POST":
form = PictureForm(request.POST ,request.FILES)
if form.is_valid():
form.save()
context = (
{'Person': Person} ,
{'form':PictureForm()}
)
return render_to_response('profile.html', context, context_instance=RequestContext(request))
UPDATE:
[.....]
board = Board.objects.get(board=picture.board)//remove this
the_id = board.id //remove this
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': picture.board.id })) // change the_id into picture.board.id
You have a typo. It should be request.FILES.
no buddy. your problem is in your model.py:
just add this function to your model
def save(self,*args, **kw):
super(PictureForm,self).save(*args, **kw)