I have an UpdateView for a model. I want to get the 'car_owner' attribute (of the Newcars model) in the UpdateView. Here's the code.
models.py
class Newcars(models.Model):
shop_no = models.ForeignKey(Shop, on_delete=models.CASCADE, default=0, related_name='newcars')
car_name = models.CharField(max_length=250)
car_owner = models.CharField(max_length=250)
def get_absolute_url(self):
return reverse('carapp:index')
def __str__(self):
return self.car_name + ' - ' + self.car_owner
views.py
(Here's the UpdateView.)
class NewcarUpdate(UpdateView):
model = Newcars
fields = ['car_name', 'car_owner']
urls.py (only the necessary part of the urlpatterns)
url(r'^newcars/(?P<pk>[0-9]+)/$', views.NewcarUpdate.as_view(), name='newcar-update'),
This is what I intend to do with the UpdateView, but cannot understand how.
class NewcarUpdate(UpdateView):
model = Newcars
fields = ['car_name', 'car_owner']
#Get the selected newcar object's 'car_owner' attribute.
#Check if the object's 'car_owner' attribute == "sometext" or not.
#If matches, only then go the normal update form.
#If doesn't, redirect to a 404 page.
add this method to your view:
def dispatch(self, request, *args, **kwargs):
if self.get_object().car_owner != "sometext":
raise Http404('Car owner does not match.')
return super(NewcarUpdate, self).dispatch(
request, *args, **kwargs)
You will need to import Http404 from django.http
You could that in the get_object method:
from django.http import Http404
# ...
class NewcarUpdate(UpdateView):
# ...
def get_object(self, queryset=None):
obj = super(NewcarUpdate, self).get_object(queryset)
if obj.car_owner == "sometext":
raise Http404
return obj
Related
I have recently learning about Validators and how they work but I am trying to add a function to my blog project to raise an error when a bad word is used.
I have a list of bad words in a txt and added the code to be in the models.py the problem is that nothing is blocked for some reason I am not sure of.
Here is the models.py
class Post(models.Model):
title = models.CharField(max_length=100, unique=True)
---------------other unrelated------------------------
def validate_comment_text(text):
with open("badwords.txt") as f:
censored_word = f.readlines()
words = set(re.sub("[^\w]", " ", text).split())
if any(censored_word in words for censored_word in CENSORED_WORDS):
raise ValidationError(f"{censored_word} is censored!")
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField(max_length=300, validators=[validate_comment_text])
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now=True)
here is the views.py:
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html" # <app>/<model>_<viewtype>.html
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, slug=self.kwargs['slug'])
comments = Comment.objects.filter(
post=post).order_by('-id')
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST or None)
if comment_form.is_valid():
content = self.request.POST.get('content')
comment_qs = None
comment = Comment.objects.create(
post=post, user=self.request.user, content=content)
comment.save()
return HttpResponseRedirect("blog/post_detail.html")
else:
comment_form = CommentForm()
context["comments"] = comments
context["comment_form"] = comment_form
context["total_likes"] = total_likes
context["liked"] = liked
return context
def get(self, request, *args, **kwargs):
res = super().get(request, *args, **kwargs)
self.object.incrementViewCount()
if self.request.is_ajax():
context = self.get_context_data(self, *args, **kwargs)
html = render_to_string('blog/comments.html', context, request=self.request)
return JsonResponse({'form': html})
return res
class PostCommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentForm
def form_valid(self, form):
post = get_object_or_404(Post, slug=self.kwargs['slug'])
form.instance.user = self.request.user
form.instance.post = post
return super().form_valid(form)
Here is my trial which didn't work
def validate_comment_text(sender,text, instance, **kwargs):
instance.full_clean()
with open("badwords.txt") as f:
CENSORED_WORDS = f.readlines()
words = set(re.sub("[^\w]", " ", text).split())
if any(censored_word in words for censored_word in CENSORED_WORDS):
raise ValidationError(f"{censored_word} is censored!")
pre_save.connect(validate_comment_text, dispatch_uid='validate_comment_text')
I am new learner so if you could provide some explanation to the answer I would be grateful so that I can avoid repeating the same mistakes.
I'm sure there are many ways to handle this, but I finally decided to adopt a common practice in all my Django projects:
when a Model requires validation, I override clean() to collect all validation logic in a single place and provide appropriate error messages.
In clean(), you can access all model fields, and do not need to return anything; just raise ValidationErrors as required:
from django.db import models
from django.core.exceptions import ValidationError
class MyModel(models.Model):
def clean(self):
if (...something is wrong in "self.field1" ...) {
raise ValidationError({'field1': "Please check field1"})
}
if (...something is wrong in "self.field2" ...) {
raise ValidationError({'field2': "Please check field2"})
}
if (... something is globally wrong in the model ...) {
raise ValidationError('Error message here')
}
The admin already takes advantages from this, calling clean() from ModelAdmin.save_model(),
and showing any error in the change view; when a field is addressed by the ValidationError,
the corresponding widget will be emphasized in the form.
To run the very same validation when saving a model programmatically, just override save() as follows:
class MyModel(models.Model):
def save(self, *args, **kwargs):
self.full_clean()
...
return super().save(*args, **kwargs)
Proof:
file models.py
from django.db import models
class Model1(models.Model):
def clean(self):
print("Inside Model1.clean()")
def save(self, *args, **kwargs):
print('Enter Model1.save() ...')
super().save(*args, **kwargs)
print('Leave Model1.save() ...')
return
class Model2(models.Model):
def clean(self):
print("Inside Model2.clean()")
def save(self, *args, **kwargs):
print('Enter Model2.save() ...')
self.full_clean()
super().save(*args, **kwargs)
print('Leave Model2.save() ...')
return
file test.py
from django.test import TestCase
from project.models import Model1
from project.models import Model2
class SillyTestCase(TestCase):
def test_save_model1(self):
model1 = Model1()
model1.save()
def test_save_model2(self):
model2 = Model2()
model2.save()
Result:
❯ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
Enter Model1.save() ...
Leave Model1.save() ...
.Enter Model2.save() ...
Inside Model2.clean()
Leave Model2.save() ...
.
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
Destroying test database for alias 'default'...
Validators run only when you use ModelForm. If you directly call comment.save(), validator won't run. link to docs
So either you need to validate the field using ModelForm or you can add a pre_save signal and run the validation there (you'll need to manually call the method, or use full_clean to run the validations).
Something like:
from django.db.models.signals import pre_save
def validate_model(sender, instance, **kwargs):
instance.full_clean()
pre_save.connect(validate_model, dispatch_uid='validate_models')
Guys I have a simple book model:
class Book(models.Model):
isbn = models.CharField(_('ISBN'),
validators=[RegexValidator('^[0-9 -]+$', message="ISBN must contains only numbers or hyphens!")] ,
max_length=13, unique=True)
title = models.CharField(_('Book\'s title'), max_length=128)
publisher = models.CharField(_('Publisher'), max_length=64)
author = models.CharField(_('Author'), max_length=64)
pages = models.IntegerField(_('Pages'), default=0)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('books:detail', kwargs={'isbn': self.isbn})
Im coding a crud system and having problem with the UpdateView.
This view works properly when I try to do a correct update, but when I insert wrong values (like letters in isbn or letters in pages) I have this error:
Reverse for 'update' with arguments '('31234-11a',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['books/update/(?P<isbn>[\\d\\-]+)/$']
EDIT:
This is my view:
class BookUpdateView(UpdateView):
"""Update the requested book."""
model = Book
form_class = BookForm
def get_object(self):
pk = self.kwargs.get(self.pk_url_kwarg, None)
queryset = self.get_queryset()
queryset = queryset.filter(isbn=self.kwargs['isbn'])
if not queryset.exists():
messages.error(self.request, 'This book doesnt exist!')
return get_object_or_404(Book, **self.kwargs)
return queryset.get()
def get_success_url(self):
messages.success(self.request, 'The book updated successfully!')
return reverse_lazy('books:detail', kwargs = {'isbn': self.object.isbn})
def form_invalid(self, form):
messages.error(self.request, 'The update has failed')
return self.render_to_response(self.get_context_data(form=form))
and my update url:
urlpatterns = [
url(r'^update/(?P<isbn>[\d\-]+)/$', view=views.BookUpdateView.as_view(), name='update'),
]
I need to use Django 1.10 in this project idk why. If someone can reccomend me some material to read, or answer the question wold be nice. Thank you
Your form_valid and form_invalid methods can not return a lazy_reverse(..) object, since that is not a HTTP response.
You can however use redirect(..) [Django-doc], which will construct a HTTP response, like:
from django.shortcuts import redirect
class BookUpdateView(UpdateView):
"""Update the requested book."""
model = Book
form_class = BookForm
# ...
def get_success_url(self):
messages.success(self.request, 'The book updated successfully!')
return reverse_lazy('books:detail', kwargs = {'isbn': self.object.isbn})
def form_invalid(self, form):
messages.error(self.request, 'The update has failed')
return redirect('books:index')
You can probably slightly improve your get_object method with:
from django.http import Http404
from django.shortcuts import redirect
class BookUpdateView(UpdateView):
"""Update the requested book."""
model = Book
form_class = BookForm
def get_object(self):
queryset = self.get_queryset()
try:
return queryset.get(isbn=self.kwargs['isbn'])
except:
messages.error(self.request, 'This book doesnt exist!')
raise Http404('Book does not exist')
It is however not very common to redirect(..) in case of a form_invalid(..). Usually the template is rerendered with the form such that the form is rendered with error messages. Therefore it is not very common to override the form_invalid method: by default Django will rerender the template with the invalid form.
EDIT: you can redirect the page to another page if the isbn does not map on a valid as follows:
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404
from django.shortcuts import redirect
class BookUpdateView(UpdateView):
"""Update the requested book."""
model = Book
form_class = BookForm
# ...
def get(self, *args, **kwargs):
try:
self.object = self.get_object()
except (ObjectDoesNotExist, Http404):
return redirect('books:index')
return self.render_to_response(self.get_context_data())
you can also just create the reverse lazy in a variable and redirect with the variable that works perfectly fine :
instead of
return reverse_lazy('books:detail', kwargs = {'isbn': self.object.isbn})
use:
url_match = reverse_lazy('books:detail', kwargs = {'isbn': self.object.isbn})
return redirect(url_match)
I've been wrestling with this all day. I'm attempting to come up with a solution to save multiple pictures per single form. I feel like I'm on the home stretch, but my issue now is I can't for the life of me get the ForeignKey to auto assign when the form is submitted. The code below is functional in every way other than no FK is applied to the DB record. Any help is greatly appreciated!
Attempting to pass:
FA_Attachments.objects.create(
image=f,field_evaluations = request.POST.get('order'))
Just gives me:
Cannot assign "'test'": "FA_Attachments.field_evaluation" must be a "FieldEvaluation" instance.
Models.py
from django.db import models
class FieldEvaluation(models.Model):
"Our default field evaluation form"
created_at = models.DateTimeField(auto_now_add=True)
order = models.CharField(max_length=50)
class FA_Attachments(models.Model):
field_evaluation = models.ForeignKey(
FieldEvaluation, on_delete=models.CASCADE, null=True)
image = models.ImageField(upload_to='photos')
Forms.py
from django import forms
from .models import FieldEvaluation, FA_Attachments
class FieldEvaluationForm(forms.ModelForm):
class Meta:
model = FieldEvaluation
fields = ['order'] # not attachments!
attachments = forms.ImageField(
widget=forms.ClearableFileInput(attrs={'multiple': True}))
Views.py
from django.views.generic.edit import CreateView
from django.shortcuts import render
from .forms import *
from .models import *
class FieldEvaluationView(CreateView):
model = FieldEvaluation
form_class = FieldEvaluationForm
template_name = 'QA.html'
success_url = '?success'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('attachments')
if form.is_valid():
for f in files:
FA_Attachments.objects.create(
image=f)
return self.form_valid(form)
else:
return self.form_invalid(form)
I would go with overriding form_valid instead of post method. In form_valid you can save your field_evaluation object and than you can assign this new object to ForeignKey field. At the end you call and return form_valid with form as argument.
class FieldEvaluationView(CreateView):
model = FieldEvaluation
form_class = FieldEvaluationForm
template_name = 'QA.html'
success_url = '?success'
def form_valid(self, form):
field_evaluation_obj = form.save(commit=True)
files = self.request.FILES.getlist('attachments')
for f in files:
FA_Attachments.objects.create(image=f, field_evaluation=field_evaluation_obj)
return super(FieldEvaluationView, self).form_valid(form)
You don't save the ForeignKey!
First of all I suggest you to make the attachment relation mandatory:
class FA_Attachments(models.Model):
field_evaluation = models.ForeignKey(
FieldEvaluation, on_delete=models.CASCADE)
image = models.ImageField(upload_to='photos')
I removed the null=True because this Model is strictly related to the FieldEvaluation
Then you must modify your POST.
class FieldEvaluationView(CreateView):
model = FieldEvaluation
form_class = FieldEvaluationForm
template_name = 'QA.html'
success_url = '?success'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('attachments')
# your form must have it!
order = request.POST.get('order')
fe = FieldEvaluation.objects.create(order=order)
if form.is_valid():
for f in files:
# here you create the relation with the FieldEvaluation
FA_Attachments.objects.create(
image=f, field_evaluation=fe)
return self.form_valid(form)
else:
return self.form_invalid(form)
I have this url is http://127.0.0.1:8000/upload/picturelist/1, which makes user_id = 1,
In my urls.py
url(r'^picturelist/(?P<user_id>\d+)$', views.pictureList),
In my view.py
def pictureList(request, user_id):
if int(user_id) != request.user.id:
raise PermissionDenied
How can I make this function based view to use createview?
class pictureList(CreateView):
You could do something like this:
In urls.py: url(r'^picturelist/(?P<user_id>\d+)$', views.MakeItView.as_view()),
In views.py:
class MakeItView(CreateView):
model = myModel
template_name = 'whatever.html'
def get_context_data(self, **kwargs):
context = super(MakeItView, self).get_context_data(**kwargs)
if int(self.kwargs['user_id']) != self.request.user.id:
raise PermissionDenied
return context
I've never used CreateView, but here's what I gather from reading the docs:
You could do it by defining form_valid:
view:
class pictureList(CreateView):
model = YourModelHere
fields = ['whatever','fields','you','want','edited']
def form_valid(self, form):
record = form.save(commit = False)
# assuming the user id is associated
# to the model with fieldname user_id
if (self.request.user == record.user_id):
record.save()
return HttpResponseRedirect(self.get_success_url())
# not sure if this works:
return self.form_invalid()
Then the template would be at 'yourappname/yourmodelhere_form.html'.
See CreateView for an example.
My form isn't saving the models that I need it to. My form:
class RewardForm(forms.Form):
quantity = forms.IntegerField(max_value=10, min_value=1, label=_('quantity'), initial=1)
reward = forms.CharField(max_length=50, label=_('reward'))
reward_denomination = forms.ModelChoiceField(queryset=Reward_Denomination.objects.all(), widget=forms.RadioSelect)
def clean_reward(self):
data = self.cleaned_data.get('reward')
try:
reward = Reward.objects.get(reward_name=data)
except ObjectDoesNotExist:
raise forms.ValidationError(_('Reward does not exist'), code='invalid')
return data
def clean_reward_denomination(self):
data = self.cleaned_data.get('reward_denomination')
try:
denomination = Reward_Denomination.objects.get(denomination=data)
except ObjectDoesNotExist:
raise forms.ValidationError(_('Denomination does not exist'), code='invalid')
return data
def save(self, request, commit=True):
user = request.user
data = self.cleaned_data
'try:
post_reward = data['reward']
post_denomination = data['reward_denomination']
quantity = data['quantity']
except LookupError:
raise Http404
reward = Reward.objects.get(reward_name=post_reward)
denomination = Reward_Denomination.objects.get(denomination=post_denomination)
user_points = Points.objects.filter(affiliate__id=user.id).aggregate(total_points=Sum('points'))
user_points = user_points['total_points']
try:
total_cost = (quantity * denomination.cost)
except ArithmeticError:
raise Http404
quote_price = -total_cost
if user_points >= total_cost:
reward_order = Points.objects.create(affiliate=user, points=quote_price, from_reward=True, from_offer=False)
status_coded = Status_Code.objects.create(short_name="Pending", name="The order is currently being reviewed", description="The order is in queue")
redeem_order = Redeem.objects.create(affiliate=user, status_code=status_coded, quantity=quantity, reward=reward, price=total_cost)
return reward_order
My Views:
class Reward_Detail(DetailView):
model = Reward
slug_field = 'reward_slug'
context_object_name = 'reward'
template_name = 'omninectar/reward.html'
#Detail Stuff
class RedeemReward(SingleObjectMixin, FormView):
template_name = 'omninectar/reward.html'
slug_field = 'reward_slug'
form_class = RewardForm
model = Reward
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(RedeemReward, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('omni:reward_confirmation')
class RewardBeautify(View):
def get(self, request, *args, **kwargs):
view = Reward_Detail.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = RedeemReward.as_view()
return view(request, *args, **kwargs)
So I initially thought that the FormView would handle the form processing (validate, and, if valid, run form.save(), etc). I'm following the FormView, SingleObjectMixin example on the Django website. I don't receive any errors when I try and submit the form, but no objects are created either. I've tried defining a form_valid method that runs the save method, I've tried putting it inside the post method in the formview, etc. Can anyone spot the error/errors? Thanks!
I'm new to view classes too and I had almost the same problem with Django 1.6.
You should add
def form_valid(self, form):
form.save()
return super(RedeemReward, self).form_valid(form)
method overriding to your RedeemReward class. This worked for me.
If you look to Django source code, you will see that there is no form saving in FormView class inheritance chain.
I am not sure if this will help or not, but I had issues finding code showing how to save the data from a FormView model. This is what I came up with. I hope it helps and you can apply it to your code.
forms.py
class JobCreateForm(forms.Form):
title = forms.CharField(label='Job Title', max_length=500)
number = forms.IntegerField(label='Job Number: ')
comps = forms.ModelMultipleChoiceField(label='Comparable Sales',
required=False, queryset=m.ComparableSale.objects.all())
details = forms.CharField(label='Job Details:', max_length=200,
required=False, widget=forms.Textarea(attrs={'rows':6, 'cols':20}))
Views.py
class JobCreateView(generic.FormView):
template_name = 'form_templates/createjob_form.html'
form_class = f.JobCreateForm
model = models.Job
success_url = '/'
def form_valid(self, form):
comps = form.cleaned_data['comps']
title = form.cleaned_data['title']
number = form.cleaned_data['number']
details = form.cleaned_data['details']
job = models.Job(title=title, number=number, details=details)
job.save()
print(comps)
if comps != []:
job.comps.add(*comps)
return super(JobCreateView, self).form_valid(form)
You can write your own ModelFormView using the mixins provided by Django (specifically, the ModelFormMixin). Then your form will be saved on a successful post.
from django.views.generic.base import TemplateResponseMixin
from django.views.generic.edit import ModelFormMixin, ProcessFormView
class BaseModelFormView(ModelFormMixin, ProcessFormView):
pass
class ModelFormView(TemplateResponseMixin, BaseModelFormView):
pass