models.py
class Post(models.Model):
pass
#property
def views_count(self):
return PostViews.objects.filter(post=self).count()
class PostViews(models.Model):
IPAddres= models.GenericIPAddressField(default="")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="post_views_count",)
date = models.DateTimeField(auto_now_add=True)
views.py
def get_posts(request, topic=""):
user = request.user
topic = topic or LATEST
topic = topic.lower()
post_type = POST_TYPE.get(topic)
posts = Post.objects.filter(status=Post.OPEN).select_related('author').prefetch_related('tags').select_related('category')
if topic == LATEST:
pass
elif post_type is not None:
posts = posts.filter(type=post_type)
elif topic == SHOW_SPAM and (user.is_authenticated and user.is_moderator):
posts = get_spam(request)
elif topic == OPEN:
posts = posts.filter(type=Post.QUESTION ,answer_count=0)
elif topic == MYTAGS and user.is_authenticated:
tags = map(lambda t: t.lower(), user.my_tags.split(","))
posts = posts.filter(tags__name__in=tags).distinct()
else:
posts = Post.objects.none()
return posts
posts.hml
{% for post in posts %}
{{ post.views_count }}
{% endfor %}
Due to the call of views, the number of sql queries grows by 10 hits.How do I get this data in place with the data received by the model Post?
I suppose you have forgot the 'context' param to pass to the HTML file, that help the DTL (Django Template Language) to read the model.
Here I write an example for you:
def get_posts(request, topic=""):
posts = Post.objects.filter(status=Post.OPEN).select_related('author').prefetch_related('tags').select_related('category').select_related('post_views_count')
context = {'post': Post.object.all()}
return render(request,'file.html',context)
I hope I've solved your problem, if not, please contact me I will try to help you more,
Your best.
Related
I've implemented a table search product with Ajax and it works well.
But now, I want to build dynamically my table taking in account the number of my warehouses can be increase.
search.js
data.forEach((item) => {
const newName = (item.nom).slice(0, 30) + "...";
tableBody.innerHTML += `
<tr>
<th>${item.sku}</th>
<td>${item.etat__etat}</td>
<td class="small">${newName}</td>
<td>${item.famille__nom}</td>
<td>${item.mageid}</td>
<td>${item.adresse}</td>
models.py (model for witch I need a set)
class SstStock(models.Model):
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
product = models.ManyToManyField(Produit)
qty = models.IntegerField()
last_update = models.DateTimeField(default=timezone.now)
views.py
def search_product2(request):
if request.method == 'POST':
search_str = json.loads(request.body).get('searchText')
products = Produit.objects.filter(sku__icontains=search_str) | Produit.objects.filter(
nom__icontains=search_str) | Produit.objects.filter(mageid__icontains=search_str)
data = products.values(
'id',
'sku',
'nom',
[...]
'sststock',
[...]
'cau_cli',
'maxsst2',
)
return JsonResponse(list(data), safe=False)
Directly in template I could do :
template
{% for produit in produits %}
{{ produit.sku}}<br>
{% for sst in produit.sststock_set.all %}
<span>{{sst.warehouse.code}} - {{ sst.qty }}</span><br>
{% endfor %}
<br>
{% endfor %}
But I couldn't find the way to pass the the sststock_set.all() in the JsonResponse. I got well a "sststock" value in it but it contains only the last value of the set instead of an array/dict of the whole set.
console.log()
qty: 7
sku: "ACP863"
sststock: 68095
68095 is the last ID of my set.
Worse, when I try to get item.sststock in the ForEach product, in my JS, it returns Undefined.
Any idea please ?
Found the way to apply #WillemVanOnsem advice with serializer.
Before all, my first error war to apply ManyToMany instead of ForeignKey on:
product = models.ManyToManyField(Produit)
After, I have set a serializer that retrieves the different stocks (warehouse_id + qty) and adds it to the Product model (with "source" parameter):
serializers.py
from rest_framework import serializers
from .models import Produit, SstStock
class StockSearchSerializer(serializers.ModelSerializer):
class Meta:
model = SstStock
fields = '__all__'
fields = ['warehouse_id', 'qty']
class ProductSearchSerializer(serializers.ModelSerializer):
sststock = StockSearchSerializer(source='sststock_set', many=True)
class Meta:
model = Produit
fields = '__all__'
To finish, I use the serializer with "many=True" in the view and return its result that will be handled by JS on my search page:
views.py
def search_product(request):
if request.method == 'POST':
search_str = json.loads(request.body).get('searchText')
products = Produit.objects.prefetch_related(
Prefetch('sststock_set',
SstStock.objects.select_related('warehouse'))
).filter(sku__icontains=search_str) | Produit.objects.filter(
nom__icontains=search_str) |
Produit.objects.filter(mageid__icontains=search_str)
serializer = ProductSearchSerializer(products, many=True)
data = serializer.data
return JsonResponse(list(data), safe=False)
And as wished, stocks array is added in the json response
There are already a lot of questions+answers regarding for loops in django, but none of the solutions work for me, so there must be something fundamentally wrong.
I have a dictionary in python/json (tried both) that I want to loop through and print.
Doing the following print a new line for each character
{% for item in data.dict %}
<p>{{item}}</p>
{% endfor %}
so something like this get's printed
{
'
N
o
d
e
'
:
The following code straight up prints nothing
{% for key, values in data.dict.items %}
<p>{{key}}</p>
{% endfor %}
Data is the name of my registered model and object is one of its variables.
In my Views.py I have something similar to this:
Data.objects.create(
dict=theDictIAmPassing
}.save
EDIT
models.py
from django.db import models
class Data(models.Model):
dict1= models.TextField()
dict2 = models.TextField()
dict3 = models.TextField()
dict4 = models.TextField()
views.py
def add(request):
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
ProjectName = form.cleaned_data['ProjectName']
date = form.cleaned_data['date']
folder = form.cleaned_data['folder']
description = form.cleaned_data['description']
myprog = program.program(folder)
createMetrics(myprog)
Entry.objects.create(
ProjectName=ProjectName,
date=date,
folder=folder,
description=description
).save()
return HttpResponseRedirect('/')
else:
form = EntryForm()
return render(request, 'myApp/form.html', {'form': form})
def createMetrics(myprog):
Metrics.objects.create(
dict1=myprog.getDict1(),
dict2=myprog.getDict2(),
dict3=myprog.getDict3(),
dict4=myprog.getDict4()
).save()
Solution found at https://stackoverflow.com/a/7469287/7761401
I needed to rewrite my Data model. Textfield (which I used because I couldn't find anything else that fits) does not suit dictionary types. Instead install django-picklefield and change type to PickledObjectField
from picklefield.fields import PickledObjectField
class Data(models.Model):
dict1 = PickledObjectField()
dict2 = PickledObjectField()
dict3 = PickledObjectField()
dict4 = PickledObjectField()
EDIT: I have since solved the problem (with help from stackoverflow and IRC, but I have to wait two days before I can mark this question as solved. See my reply below to see the solution!
I am currently working an a 'IMDB' clone for me and my friends to use. The idea is that each of us gets an account and can either submit new movies or vote on already submitted ones. These movies are sorted by rating and users can access the detail view to see the individual reviews.
To achieve this I'm using foreign keys in my models.py - one for the movie entries (with information like director, title, etc) and one for the individual votes. The latter one uses foreign keys to fetch the movies title and the user that submitted the review.
However when testing the form that submits reviews I encountered the 'NOT NULL constraint failed: list_vote.voter_id_id' error. When browsing through the error page I discovered that the foreignkey values are not submitted to the database
params: [None, None, 9.0]
query: ('INSERT INTO "list_vote" ("movie_id_id", "voter_id_id", "rating") VALUES (?, ' '?, ?)')
self <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x7f77b8907dc8>
Yet, when I browse the error page for the local vars at the moment of the 'post.save()' command the missing variables seem to be there
entry: <Entry: Starwars 4>
form: <VoteForm bound=True, valid=True, fields=(rating)>
movie_id: 'Starwars 4'
post: <Vote: Vote object>
request: <WSGIRequest: POST '/vote/starwars-4/'>
slug: 'starwars-4'
voter_id : <SimpleLazyObject: <User: admin>>
If I add the ' movie_id' and ' user_id' values to my modelform (in the 'fields' variable) the form actually works. (though this is not what I want, as there could potentially be hundreds of movies in this database, making selection rather difficult. And right now, any user can pretend to be anyone)
I'm probably missing something very obvious, but for the life of me I cannot figure out what I'm doing wrong. The models, forms etc are based on both the Django_girls tutorial (where they work) and the 'Hello WebApp' book (though foreign keys are not used in this book)
Does anyone know what I'm doing wrong here?
These are the models used:
class Entry(models.Model):
movie = models.CharField(max_length=255)
director = models.CharField(max_length=255)
total_votes = models.IntegerField(default=0)
rating = models.FloatField(default=0)
length = models.IntegerField(default=0)
year = models.IntegerField(default=0)
added_by = models.ForeignKey(User)
slug = models.SlugField(unique=True)
def __str__(self):
return self.movie
########################
Vote-model:
########################
class Vote(models.Model):
class Meta:
unique_together = (('voter_id','movie_id'),)
movie_id = models.ForeignKey(Entry)
voter_id = models.ForeignKey(User)
rating = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(10)])
This is my form.py:
class VoteForm(ModelForm):
class Meta:
model = Vote
fields = ('rating',)
This is the relevant function form the view.py:
def lets_vote(request, slug):
entry = Entry.objects.get(slug=slug)
if request.method == "POST":
form = VoteForm(request.POST)
if form.is_valid():
voter_id = request.user
movie_id = Entry.objects.get(slug=slug).movie
post = form.save(commit=False)
post.save()
return redirect('/movies/')
else:
form = VoteForm()
return render(request, 'list/lets_vote.html', {'entry':entry, 'form': form})
Last but not least: the voteform from the html page:
<form role="form" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% endblock %}
I posted this question on the Django IRC as well and somebody there helped me solve the problem!
I originally made two mistakes:
I added a '_id' in my models. This is already done so automatically, so the variables in my database were actually called 'movie_id_id'. I've since removed the superfluous '_id'.
The '.movie' in the following line was unnecessary.
post.movie = Entry.objects.get(slug=slug).movie
The correct, working view is as follows:
def lets_vote(request, slug):
entry = Entry.objects.get(slug=slug)
form = VoteForm(request.POST)
if request.method == "POST":
if form.is_valid():
post = form.save(commit=False)
post.voter = request.user
post.movie = Entry.objects.get(slug=slug)
post.save()
return redirect('/movies')
I only needed the actual instance, but the rest of #Alasdair his suggestions were correct.
Thank you #Alasdair and Flobin from IRC!
I'm trying to create an application where users can send each other messages.
The function I am working on is called read, allows the user to read the message he receives.
The way my model works in the following manner: Every message is related to a thread and this will used to keep track of replied messages related to each other.
My function works by capturing the message id and filtering all the messages related to the message thread. Then I will populate a form with the current message id and allow the user to reply to the form.
When the user submits via POST, I will retrieve the hidden message id and create a new message using the retrieved message id thread.
The issue: I can't figure out how to raise an error for such situation when exceeding the character limit and populating the current message id with the raised error. Can someone kindly help me?
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=30, blank=True)
def __unicode__(self):
return self.user.username
class Thread(models.Model):
subject = models.CharField(max_length=100, blank=True)
user = models.ForeignKey(User)
class Message(models.Model):
user = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
created = models.DateTimeField(auto_now_add=True)
body = models.CharField(max_length=1000)
read = models.BooleanField(default=False)
sentmessage = models.BooleanField(default=False)
thread = models.ForeignKey(Thread)
draft = models.BooleanField(default=False)
def __unicode__(self):
return self.body
Views:
#login_required
def read(request,id):
try:
messages = Message.objects.get(pk=id,recipient=request.user)
except Message.DoesNotExist:
return HttpResponseRedirect(reverse('world:Display'))
messages.read = True
messages.save()
if request.method =='POST':
form = ReplyForm(request.POST)
if form.is_valid():
id = request.POST.get('hidden_field', False)
try:
messages = Message.objects.get(pk=id ,recipient=request.user,sentmessage=True,draft=False)
except Message.DoesNotExist or Thread.DOesNotExist:
return HttpResponseRedirect(reverse('world:LoginRequest'))
person = Person.objects.get(user=messages.user)
if person.inbox == "D":
return HttpResponseRedirect(reverse('world:message'))
body = form.cleaned_data['body']
Message.objects.create(user=request.user,recipient=messages.user,body=body,thread=messages.thread,sentmessage=True,read=False)
return HttpResponseRedirect(reverse('world:message'))
message = Message.objects.filter(thread=messages.thread ).filter(created__lte=messages.created)
person = Person.objects.get(user=request.user)
initial = {}
initial.update({'hidden_field': messages.id})
form = ReplyForm(initial=initial)
return render(request,'read.html',{'messages':messages,'form':form,'message':message,'person':person})
forms
class ReplyForm(forms.Form):
body = forms.CharField(widget=forms.Textarea,required=False,max_length=555)
hidden_field = forms.CharField(widget=forms.HiddenInput())
Template:
<div class="box22">
{% for m in message %}
<div class="wrapper">
<div class="user">{{m.user.username}} </div>
<div class="message">{{m.body}}</div>
</div>
{% endfor %}
<form method="POST" >{% csrf_token %}
{{form.body}}{{form.hidden_field}}
<input type = "submit" value= "send" class="sen"/>
</form>
{{form.body.errors}}
First of all, I recommend you use only form.cleaned_data to get submited data.
id = form.cleaned_data['hidden_field']
Also, why you reassign id from request.POST data when you already have id defined in your function args? Maybe it should look like:
msg_response_id = form.cleaned_data['hidden_field']
Then, you always need to check hidden_field value and you can do it with your custom clean method. Add clean_hidden_field method to your form and also you should override init form method and pass id and msg_response_id.
In your views:
form = ReplyForm(initial=initial, id=id, msg_response_id=msg_response_id)
And forms:
class ReplyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.id = kwargs.pop('id', None)
self.msg_response_id = kwargs.pop('msg_response_id', None)
super(ReplyForm, self).__init__(*args, **kwargs)
hidden_field = forms.CharField(widget=forms.HiddenInput())
def clean_hidden_field(self):
if self.id and self.msg_response_id and self.id == self.msg_response_id:
# Raise form error, which will appear in your template.
raise forms.ValidationError(_('You can't reply to your own message.'))
The last paragraph describing what you want is not all too clear, but I believe what you are trying to do, is make an raise an exception when a certain requirement is not met in your form.
If that is the case, then simply catching an ValidationError which is inbuilt into django will do the trick.
So, something like this:
try:
form = ReplyForm(request.POST)
except ValidationError:
# do what you want here
Alternatively,
form = ReplyForm(request.POST)
if form.is_valid():
# do stuff
else:
raise ValidationError
If this does not work for you, then you might try validation the post-data without using django-forms, but usually django takes care of this problem for you, and it will automatically generate the error messages. You can take a deeper look # form validation here.
However if you want even more fine grain control you can make the form yourself, and handle the validation within your views. Otherwise, ValidationError should do the trick and let you redirect the error messages.
I am trying to realize a Class Based ListView which displays a selection of a table set. If the site is requested the first time, the dataset should be displayed. I would prefer a POST submission, but GET is also fine.
That is a problem, which was easy to handle with function based views, however with class based views I have a hard time to get my head around.
My problem is that I get a various number of error, which are caused by my limited understanding of the classed based views. I have read various documentations and I understand views for direct query requests, but as soon as I would like to add a form to the query statement, I run into different error. For the code below, I receive an ValueError: Cannot use None as a query value.
What would be the best practise work flow for a class based ListView depending on form entries (otherwise selecting the whole database)?
This is my sample code:
models.py
class Profile(models.Model):
name = models.CharField(_('Name'), max_length=255)
def __unicode__(self):
return '%name' % {'name': self.name}
#staticmethod
def get_queryset(params):
date_created = params.get('date_created')
keyword = params.get('keyword')
qset = Q(pk__gt = 0)
if keyword:
qset &= Q(title__icontains = keyword)
if date_created:
qset &= Q(date_created__gte = date_created)
return qset
forms.py
class ProfileSearchForm(forms.Form):
name = forms.CharField(required=False)
views.py
class ProfileList(ListView):
model = Profile
form_class = ProfileSearchForm
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def post(self, request, *args, **kwargs):
self.show_results = False
self.object_list = self.get_queryset()
form = form_class(self.request.POST or None)
if form.is_valid():
self.show_results = True
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
else:
self.profiles = Profile.objects.all()
return self.render_to_response(self.get_context_data(object_list=self.object_list, form=form))
def get_context_data(self, **kwargs):
context = super(ProfileList, self).get_context_data(**kwargs)
if not self.profiles:
self.profiles = Profile.objects.all()
context.update({
'profiles': self.profiles
})
return context
Below I added the FBV which does the job. How can I translate this functionality into a CBV?
It seems to be so simple in function based views, but not in class based views.
def list_profiles(request):
form_class = ProfileSearchForm
model = Profile
template_name = 'pages/profile/list_profiles.html'
paginate_by = 10
form = form_class(request.POST or None)
if form.is_valid():
profile_list = model.objects.filter(name__icontains=form.cleaned_data['name'])
else:
profile_list = model.objects.all()
paginator = Paginator(profile_list, 10) # Show 10 contacts per page
page = request.GET.get('page')
try:
profiles = paginator.page(page)
except PageNotAnInteger:
profiles = paginator.page(1)
except EmptyPage:
profiles = paginator.page(paginator.num_pages)
return render_to_response(template_name,
{'form': form, 'profiles': suppliers,},
context_instance=RequestContext(request))
I think your goal is trying to filter queryset based on form submission, if so, by using GET :
class ProfileSearchView(ListView)
template_name = '/your/template.html'
model = Person
def get_queryset(self):
name = self.kwargs.get('name', '')
object_list = self.model.objects.all()
if name:
object_list = object_list.filter(name__icontains=name)
return object_list
Then all you need to do is write a get method to render template and context.
Maybe not the best approach. By using the code above, you no need define a Django form.
Here's how it works : Class based views separates its way to render template, to process form and so on. Like, get handles GET response, post handles POST response, get_queryset and get_object is self explanatory, and so on. The easy way to know what's method available, fire up a shell and type :
from django.views.generic import ListView if you want to know about ListView
and then type dir(ListView). There you can see all the method defined and go visit the source code to understand it. The get_queryset method used to get a queryset. Why not just define it like this, it works too :
class FooView(ListView):
template_name = 'foo.html'
queryset = Photo.objects.all() # or anything
We can do it like above, but we can't do dynamic filtering by using that approach. By using get_queryset we can do dynamic filtering, using any data/value/information we have, it means we also can use name parameter that is sent by GET, and it's available on kwargs, or in this case, on self.kwargs["some_key"] where some_key is any parameter you specified
Well, I think that leaving validation to form is nice idea. Maybe not worth it in this particular case, because it is very simple form - but for sure with more complicated one (and maybe yours will grow also), so I would do something like:
class ProfileList(ListView):
model = Profile
form_class = ProfileSearchForm
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def get_queryset(self):
form = self.form_class(self.request.GET)
if form.is_valid():
return Profile.objects.filter(name__icontains=form.cleaned_data['name'])
return Profile.objects.all()
This is similar to #jasisz 's approach, but simpler.
class ProfileList(ListView):
template_name = 'your_template.html'
model = Profile
def get_queryset(self):
query = self.request.GET.get('q')
if query:
object_list = self.model.objects.filter(name__icontains=query)
else:
object_list = self.model.objects.none()
return object_list
Then all you have to do on the html template is:
<form method='GET'>
<input type='text' name='q' value='{{ request.GET.q }}'>
<input class="button" type='submit' value="Search Profile">
</form>
This has been explained nicely on the generic views topic here Dynamic filtering.
You can do filtering through GET, I don't think you can use POST method for this as ListView is not inherited from edit mixings.
What you can do is:
urls.py
urlpatterns = patterns('',
(r'^search/(\w+)/$', ProfileSearchListView.as_view()),
)
views.py
class ProfileSearchListView(ListView):
model = Profile
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def get_queryset(self):
if len(self.args) > 0:
return Profile.objects.filter(name__icontains=self.args[0])
else:
return Profile.objects.filter()
I think that the error you are getting is because your form doesn't require the name field. So, although the form is valid, the cleaned_data for your name field is empty.
These could be the problematic lines:
if form.is_valid():
self.show_results = True
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
If I were you, I would try changing the line:
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
to this:
self.profiles = Profile.objects.none()
If you stop receiving errors (and your template receives an empty object_list), the problem you have is what I said before: name field not required.
Let us know if this doesn't work!
Search on all fields in model
class SearchListView(ItemsListView):
# Display a Model List page filtered by the search query.
def get_queryset(self):
fields = [m.name for m in super(SearchListView, self).model._meta.fields]
result = super(SearchListView, self).get_queryset()
query = self.request.GET.get('q')
if query:
result = result.filter(
reduce(lambda x, y: x | Q(**{"{}__icontains".format(y): query}), fields, Q())
)
return result
def get_queryset(self):
query_name = self.request.GET.get('query', '')
object_list = Product.objects.filter(
Q(title__icontains=query_name)
)
return object_list
<form action="" method="GET">
{% csrf_token %}
<input type="text" name="query" placeholder="Search keyword">
<i class="ti-search"></i>
</form>