Django ListView count all keys in a JSON variable - json

I would like to return in my Link.html the number of links contained in allLinks JSON variable.
So far I guess I misunderstand the use of get_context_data() and how to pass in context['CountLink'] the total count of links for each Post.
With the current code, I got:
Liste des recherches
terre : <QuerySet [<Post: terre>, <Post: océan>]> Links
océan : <QuerySet [<Post: terre>, <Post: océan>]> Links
models.py
class Post(models.Model):
title = models.CharField(max_length=255)
url = models.URLField(max_length=255)
allLinks = models.JSONField()
def __str__(self):
return self.title
views.py
class LinkView(ListView):
model = Post
template_name = 'link.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['CountLink'] = Post.objects.all()
return context
Link.html
{% for post in object_list %}
<li>
{{ post.title }} :
{{CountLink}} Links
</li>
{% endfor %}
Example of allLinks: {"0": "github.com/kubernetes/kubernetes/releases/tag/v1.26.0", "1": "kubernetes.io/docs/concepts/overview/what-is-kubernetes",}

Use the built-in length template filter:
{{ post.allLinks|length }} Links

You can use count()
In your case I think you want something like that, it will return count of Post model objects
context['CountLink'] = Post.objects.all().count()
For more info check this

Related

Django How to Post comment on only one List item

I am trying to create django commerce app I am little bit stuck on a thing
When I post comment via form I created
<form action="{% url 'comment' list_id.id %}" method="POST">
{% csrf_token %}
<textarea name="comment" class="inp-cmt" rows="3"></textarea>
<input type="submit">
</form>
the comment is posted but it post on all of my list page I wanted only on the page where comment is posted
my comment section
{% if allcomments %}
<h1>Comments</h1>
<div class="card-cmt">
{%for com in allcomments%}
<li style="list-style: none;">
<footer class="post-info">
<span>{{com.user}}</span>
<p>{{com.text}}</p>
</footer>
</li>
{% endfor %}
</div>
{% endif %}
my urls
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("newlist", views.create_listing, name="new_list"),
path("item", views.add_item, name="new_item"),
path("listing/<int:list_id>", views.listing, name="listing"),
path("delete/<int:item_id>", views.delete_list, name="delete"),
path("comment/<int:list_id>", views.comments, name="comment")
]
my views for comment and listing
def comments(request, list_id):
coms = Comments()
if request.method == 'POST':
coms.user = request.user.username
coms.text = request.POST.get('comment')
coms.listid = list_id
coms.save()
return redirect('listing', list_id)
else :
return redirect('index')
def listing(request, list_id):
list_item = Listing.objects.get(id=list_id)
return render(request, "auctions/listing.html",{
"list_id" : list_item,
"allcomments" : Comments.objects.all()
})
models
class Listing(models.Model):
owner = models.CharField(max_length =64,default="N/A")
productname = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
description = models.CharField(max_length=999, default="test")
date = models.DateField(auto_now_add=True)
link = models.CharField(max_length=200, default="test1")
def __str__(self):
return f"{self.owner} {self.productname} {self.price} {self.date} {self.description} {self.link}"
class Comments(models.Model):
user = models.CharField(max_length=64)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)
listid = models.IntegerField(default=0)
def __str__(self):
return f"{self.user} {self.text} {self.date} {self.listid}"
You're returning all comments on every listing when you do "allcomments" : Comments.objects.all()
The problem is in your listing function. Try this instead:
def listing(request, list_id):
list_item = Listing.objects.get(id=list_id)
return render(request, "auctions/listing.html",{
"list_id" : list_item,
"allcomments" : Comments.objects.filter(listid=list_id)
})
Notice the change - from "allcomments" : Comments.objects.all() to "allcomments" : Comments.objects.filter(listid=list_id)
Also, your implementation for class Comments and class Listing could be a bit better. Have you ever come across something called a ForeignKey? It will be a lot more efficient to use that. https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ForeignKey

how to pass value from html to view in django?

I have made this HTML code:
<h3>your major is {{user.userprofile.major}}</h3>
This will correctly show the major on the webpage, but I want to use this string to get something from another table in view.
How would I pass this string to view?
edit:
Here is my view.py
def dashboardView(request):
obj = BooksFile.objects.all()
query = BooksFile.objects.filter(book_major='cs)
return render(request, 'dashboard.html', {'books': obj, 'major': query})
def registerView(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
profile_form = UserProfileForm(request.POST)
if form.is_valid() and profile_form.is_valid():
user = form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
return redirect('login_url')
else:
form = UserCreationForm()
profile_form = UserProfileForm()
context = {'form': form, 'profile_form': profile_form}
return render(request, 'registration/register.html', context)
here is my template:
{% extends 'index.html' %}
{% block content %}
<h1>Welcome, {{user.username}}</h1>
<h2>Your major is {{user.userprofile.major}}</h2>
{% for book in books %}
<h3>Your book name is {{book.book_name}}</h3>
{% endfor %}
{% endblock %}
I am trying to show the book names from the booksfile table by corresponding major that user has. Right its showing the books that has "cs" attribute because I manually put "cs" in the get function in view. I am trying to send the major string from template to view, so that I can put what ever the user's major is in the get function. Or is there any other way to do it.
You need to use a form in your template and submit it to call your view. i.e.
<form action="your_view_url" method="POST">
<input type="text" name="major" value="{{user.userprofile.major}}"/>
<input type="submit"/>
</form>
an then in your view you access that with:
if request.POST:
major = request.POST.get('major')
As per documentation: https://docs.djangoproject.com/en/2.2/topics/forms/
First of all you have to get the value of model with help of queryset, and put it in the dictionary and then pass it with the template.
In views:
def get(self, request):
queryset = Model_name.objects.all()
ctx = {
'queryset': queryset,
}
return render(request, 'page_name(or template_name).html', ctx)
in template:
<form action="{%url'(your_view_name without brackets)'%}" method="POST">
{% for data in queryset%}
<span class="username">{{data.name(field of your model)}} .
</span>
<span class="email">{{data.email(field of your model)}} .
</span>
{% endfor%}
</form>

Unable to link blog post to its content page in Wagtail

I'm having a problem creating a link of a Blog Post to its own content page in wagtail. In my models I have two page classes, BlogPage and IndexPage. My BlogPage class is used to create the blog post, and IndexPage class is used to display a list of blog posts.
Please see models below:
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailsearch import index
class IndexPage(Page):
intro = RichTextField(blank=True)
def child_pages(self):
return BlogPage.objects.live()
content_panels = Page.content_panels + [
FieldPanel('intro', classname='full'),
]
subpage_types = ['blog.BlogPage']
class BlogPage(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
search_fields = Page.search_fields + (
index.SearchField('intro'),
index.SearchField('body'),
)
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('intro'),
FieldPanel('body', classname="full")
]
My challenge is that I can't figure out how to link the blog post on the Index Page to its own page. Do I need to create a separate page model and html template to achieve this? or what could be the best approach to solve this problem?
You can create an include template (it doesn't need a model) - let's name it truncated_blog_post.html - which you can then invoke in your index_page.html template. This would be the recommended approach because using a include template for a post gives the possibility to use it anywhere you need to display a list of (truncated usually) posts: when you want the posts under a certain tag, for example.
truncated_blog_post.html
{% load wagtailcore_tags %}
<article>
<h2>{{ blog.title }}</h2>
<p>{{ blog.date }}</p>
<p>{{ blog.body|truncatewords:40 }}</p>
</article>
Using the pageurl tag from wagtailcore_tags you get the relative URL of that blog post. Obviously, if you don't want to create a include template for a truncated post, you can put the article code from blog_post.html directly in the for loop in the index_page.html template.
And your index_page.html template:
....
{% for blog in blogs %}
{% include "path/to/includes/truncated_blog_post.html" %}
{% empty %}
No posts found
{% endfor %}
....
For this to work you have to modify the IndexPage model:
class IndexPage(Page):
intro = RichTextField(blank=True)
#property
def blogs(self):
blogs = BlogPage.objects.live()
return blogs
def get_context(self, request):
# Get blogs
blogs = self.blogs
# Update template context
context = super(IndexPage, self).get_context(request)
context['blogs'] = blogs
return context
content_panels = Page.content_panels + [
FieldPanel('intro', classname='full'),
]
subpage_types = ['blog.BlogPage']

Flask-SQLAlchemy queries

I am having issues with a seemingly simple sqlalchemy query using Flask.
I have a table called Links and within that table there are columns called 'id', 'author_id', 'link', and 'group'. My models.py looks like this:
class Links(db.Model):
__tablename__='links'
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
link = db.Column(db.String, unique=False, nullable=True)
group = db.Column(db.String, unique=False, nullable=False)
def __init__(self, author_id=None, link=None, group=None):
self.author_id = author_id
self.link = link
self.group = group
def __repr__(self):
return'<Link %r>' %(self.link)
I would like to return the values of all groups associated with the user that is logged into the application. Here is my views.py file:
#app.route('/members/', methods=['GET','POST'])
#login_required
def members():
error=None
form = PostLink(request.form, csrf_enabled = False)
uid = session['user_id']
link = "NULL"
groups = Links.query.filter_by(author_id=uid).all()
if request.method=='POST':
if form.validate_on_submit():
new_group = Links(
uid,
form.group.data,
link,
)
try:
db.session.add(new_group)
db.session.commit()
flash("You have created a group!")
except IntegrityError:
error = 'That group did not work, maybe it already exists?'
else:
flash_errors(form)
return render_template('members.html', form=form, error=error, link = link, groups=groups)
And my 'members.html':
{% extends "base.html" %}
{% block content %}
<p>Add New Group: {{ form.group }}</p>
<input id="link" type="hidden" name="link" value= {{ link }}/>
<p><input type="submit" value="Request"></p>
</form>
<br/>
{% for group in groups %}
<li><p>
{{ group }}
</p></li>
{% endfor %}
{% endblock %}
Currently this is just returning a list of links and groups in an odd format:
<Link u'link2'>
<Link u'linky'>
<Link u'linkymaybe'>
<Link u'madeit'>
<Link u'BLAH'>
So the core of my question is how do I build a query using SQLAlchemy to display all groups associated with the logged in user (uid = session['user_id']) I am pretty new to Flask and this problem is becoming an issue as I have tried a number of filter_by and filter statements with no luck at all.
Thank you in advance!
It is displaying correctly the object "Link" returned by the query.
You need to format it in the template.
Thisi is link {{ group.link }} from author #{{ group.author_id }} in group named {{ group.group }}
Maybe you've chosen a bad name "group" when cycling on results in the template. It should be called link.
In the template, you can show the group name using {{ link.group }} instead of {{ link }}. The query is returning the whole object.

HTML input textbox in Django admin.py filter

I would like to filter data in Django (admin.py) with text writen in HTML input textbox. I need to filter companies by city in which they are and list of all cities is too long. I would like to replace list of all cities in filter by one text input. I found something similar
here http://djangosnippets.org/snippets/2429/ but there are two problems:
author did not posted models.py, so it is difficuilt to change code for my needs (+ no comments)
there is used class UserFieldFilterSpec(RelatedFilterSpec): but I need to use AllValuesFilterSpec instead of RelatedFilterSpec (more in file django/contrib/admin/filterspecs.py), because list of towns are in the same class as comapny (there shoud by class of towns and they should be referencing to company by foreign key (ManyToMany relationship), but for some reasons it have to be done this way)
important part of models.py looks something like this
class Company(models.Model):
title = models.CharField(max_length=150,blank=False)
city = models.CharField(max_length=50,blank=True)
and something from admin.py
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = ['city',]
So again, I need to:
1. instead of list od cities display one text input in Django filter
2. After inputing city neme in that text input, filter data by city (request for filtering can be sent with some submit button or through javascript)
Thank yoy for all posts.
In case anybody still need this. It is little hackish in template, but implemented without a piece of js.
filters.py:
from django.contrib.admin import ListFilter
from django.core.exceptions import ImproperlyConfigured
class SingleTextInputFilter(ListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, request, params, model, model_admin):
super(SingleTextInputFilter, self).__init__(
request, params, model, model_admin)
if self.parameter_name is None:
raise ImproperlyConfigured(
"The list filter '%s' does not specify "
"a 'parameter_name'." % self.__class__.__name__)
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html:
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Then according to your models in admin.py:
class CatalogCityFilter(SingleTextInputFilter):
title = 'City'
parameter_name = 'city'
def queryset(self, request, queryset):
if self.value():
return queryset.filter(city__iexact=self.value())
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = [CatalogCityFilter,]
Ready to use filter would look like this.
I'm running Django 1.10, 1.11 and r_black's solution didn't completely fit because Django was complaining that filter fields must inherit from 'FieldListFilter'.
So a simple change for the filter to inherit from FieldListFilter took care of Django complaining and not having to specify a new class for each field, both at the same time.
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
if self.value():
return queryset.filter(imei__icontains=self.value())
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html (unchanged):
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Usage:
class MyAdmin(admin.ModelAdmin):
list_display = [your fields]
list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields]
While it's not actually your question, this sounds like a perfect solution for Django-Selectables you can with just a few lines add an AJAX powered CharField Form that will have it's entries selected from the list of cities. Take a look at the samples listed in the link above.
Below is the fix for field name..in queryset function
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
variable_column = self.parameter_name
search_type = 'icontains'
filter = variable_column + '__' + search_type
if self.value():
return queryset.filter(**{filter: self.value()})
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': ('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )