Django-CMS custom plugin not showing data in published pages - python

I created a custom plugin for my project in Django/Django-CMS and the plugin has a list of testimonials that the user pick when adding the plugin to the page. The model is this:
class TestimonialsPlugin(CMSPlugin):
n_testimonials = models.PositiveIntegerField(
verbose_name=_('Number of Testimonials'), default=5)
speed_autoplay = models.PositiveIntegerField(
verbose_name=_('Speed of slider (milliseconds)'), default=3000)
picked_testimonials = models.ManyToManyField(Testimonials,
verbose_name=_('picked_testimonials'),
blank=True, null=True)
In the edit mode I can se the testimonials in my page and I can publish without errors but when I see the published page the testimonials doesn't show. The plugin template is being rendered but the picked_testimonials gives None. Here's the template:
<div class="max-width1440 block clearfix relative">
<div class="small-only-text-left small-12 small-offset-0 medium-text-center medium-offset-1 medium-10 large-offset-1 large-10 column pt-px60 pb-px40 pl-px40 pr-px40 slider-testimonials">
{% for testimonial in instance.picked_testimonials.all %}
{% if forloop.counter0 < instance.n_testimonials %}
<div class="slider column">
<blockquote class="acta_mediumitalic size36 pl-px80 pr-px80 line-height140">
{% render_model testimonial "description" %}
</blockquote>
<div class="mt-px30">
<p class="acta_book size20 softblack">{{ testimonial.author }},<span>{{ testimonial.city }}</span>
</p>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>

When a page is published, a copy of a plugin is created - which does include plugin's fields but not any relations. There is a provision in the CMS to provide copy method for exactly this purpose, see example from documentation:
class ArticlePluginModel(CMSPlugin):
title = models.CharField(max_length=50)
sections = models.ManyToManyField(Section)
def copy_relations(self, oldinstance):
self.sections = oldinstance.sections.all()
The important bit is the copy_relations, which ensures that the new object has the same relational links.
In your case, something along these lines should work:
class TestimonialsPlugin(CMSPlugin):
n_testimonials = models.PositiveIntegerField(
verbose_name=_('Number of Testimonials'), default=5)
speed_autoplay = models.PositiveIntegerField(
verbose_name=_('Speed of slider (milliseconds)'), default=3000)
picked_testimonials = models.ManyToManyField(Testimonials,
verbose_name=_('picked_testimonials'),
blank=True, null=True)
def copy_relations(self, oldinstance):
self.picked_testimonials = oldinstance.picked_testimonials.all()

Related

How can i add something to the database with a submit button in Django?

I'm making a grocery list web app in django and i have a page with your groceries list and i have a page with all the products you can add to your list.
every product has a button "add to list". The intention is that when you click on that button that that product automatically becomes added to the groceries list. Does someone know how to do that? thank you in advance.
The Groceries List page
The all products page
models.py
from django.db import models
# Create your models here.
class Brand(models.Model):
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class AllProducts(models.Model):
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class ShoppingList(models.Model):
product = models.ForeignKey(AllProducts, null=True, on_delete= models.SET_NULL, blank=True)
brand = models.ForeignKey(Brand, null=True, on_delete= models.SET_NULL, blank=True)
quantity = models.CharField(max_length=200, null=True, blank=True)
info = models.TextField(max_length=500, null=True, blank=True)
def __str__(self):
return self.product
The class brand is a class with all the brands of the products.
The class All_Products is a class with all the products that you can add to your groceries list.
And the class ShoppingList is a class with all the products in the groceries list.
Views.py
def home(request):
products = ShoppingList.objects.all()
context = {
'products':products,
}
return render(request, 'groceries_list/home.html', context )
def all_products(request):
all_products = AllProducts.objects.all()
context = {
'products':all_products,
}
return render(request, 'groceries_list/all_products.html', context)
The home function is the function that handels the groceries list page an the all_products function is the function that handels the page with all the product you can add to your list.
groceries list template
{% extends "groceries_list/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="card m-1 text-white">
Add Grocery
</div>
{% for product in products %}
<div class="item-row">
<div class="card m-1 text-white" style="background-color: #9BD6E0;">
<div class="card-body">
<a class="btn btn-sm btn-info" href="{% url 'update_gorcery' product.id %}">Update</a>
<a class="btn btn-sm btn-danger" href="{% url 'delete_gorcery' product.id %}">Delete</a>
<span class="text-dark"><strong>{{product.product}}</strong> {{product.quantity}} </span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
all products template
% extends "groceries_list/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="card m-1 text-white">
Add Product
</div>
{% for product in products %}
<div class="item-row">
<div class="card m-1 text-white" style="background-color: #9BD6E0;">
<div class="card-body">
<button type="submit" class="btn btn-success btn-sm ">Add To List</button>
<span class="text-dark ml-3 text-center"><strong>{{product.name}}</strong>
<a class="btn btn-sm btn-danger float-right" href="{% url 'delete_product' product.id %}">Delete</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
The site may have many users using it at the same time, so you should arrange for a way to identify the particular user who adds the item to the shopping list. The easiest way to do this is to create an authentication system (You can use as-is or extend the User model) from django.auth.models
Looking at your views, as they are, both the home() and and all_products() view are rendering the same context, which definitely can't be what is in the cart (for the home view).
A way to handle this would be to make your ShoppingList model in a way that it includes a field for the customer. The customer would be the user making the request.
In the all_products.html page, you can create a form with a hidden field that you pre-populate with the product.id and the 'add to list' button as a submit button for this form.
When a user clicks 'add to list', the form gets posted to a url that invokes the responsible view. In that view, you create a 'ShoppingList object' (an instance of the ShoppingList model that you created in models) with values of the product.id that was posted by the form and the customer as request.user (the user making the request).
Just a few random tips:
In your ShoppingList model, you are defining quantity as a CharField but quantity is best defined as an IntegerField. Also, there is no need to use both blank=True and null=True arguments. I personally like to use blank=True only due to safety reasons that I won't talk about here.
I would also recommend that you revise the naming systems for your models and views.
In summary:
Add field customer to the ShoppingList model.
Make field product a CharField.
Make a form with a hidden field that posts back the product.id when a
user clicks 'add to list'.
Handle the post request by making a ShoppingList object.
Consider making quantity an IntegerField.

Reporting in django

I need some advice on what to do. I am developing an app for a course at my university. The userstory I working on at the moment is: "As a user I want to be able to report films that doesn´t exist."
My thought was to have a report-button at my film_detail.html, where clicking this button would trigger the BooleanField in my model, and mark the film as reported. When clicking the report-button I was thinking on just having a pop-up window to confirm the reporting, and I believe I won´t need to create a complete new view for this(?).
Does anyone have any idea on how to do this (cause I´m kinda stuck), or maybe having a better idea?
**models.py**
class Film(models.Model):
title = models.CharField(max_length=100)
title_short = models.CharField(max_length=17, default=None, null=True)
plot = models.TextField()
poster = models.ImageField(default="default.png", upload_to="posters")
release_date = models.DateField(blank=True, null=True)
date_posted = models.DateTimeField(default=timezone.now)
reported = models.BooleanField("Is reported", default=False)
#class Admin:
# list_display = ("is_reported")
#def is_reported(self):
# return self.reported == True
#is_reported.BooleanField = False
**HTML**
{% extends "board/base.html" %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle film-img" src="/media/{{object.poster}}">
<!-- Mulighet for å ha en "add review"-knapp på siden der hvor filmene vises. -->
<!-- <i class="material-icons right">rate_review</i>add review -->
<a onclick="myFunction()" class="waves-effect waves-light red darken-4 btn"><i class="material-icons right">report</i>report</a>
<script>
function myFunction() {
alert("Are you sure you want to report this film?");
}
</script>
<div class="media-body">
<h2 class="film-title">{{ object.title }}</h2>
<p class="film-plot">{{ object.plot }}</p>
</div>
</article>
{% endblock content %}
There are definitely different ways on implementing it. One way is to expose an API with a url like: films/report with PUT method. After getting confirmation from the user, you can easily send an Ajax request.

Django Getting Data From Foreign Key

Im a newbie working on a news site (or at least trying to, a lot of "problems" in the last few days lol ) trying to learn Django the best I can.
This is what I want to do :
I have an Article Model, it used to have 6 image fields that I used to send to the template and render the images, each image field had its own name and all was well in the world.
Then I got tasked with puting the Article images in a separate Image model.
So I did this :
class Article(models.Model):
title = models.CharField('title', max_length=200, blank=True)
slug = AutoSlugField(populate_from='title', default="",
always_update=True, unique=True)
author = models.CharField('Author', max_length=200, default="")
description = models.TextField('Description', default="")
is_published = models.BooleanField(default=False)
article_text = models.TextField('Article text', default="")
pub_date = models.DateTimeField(default=datetime.now, blank=True)
article_category = models.ForeignKey(Category, on_delete="models.CASCADE", default="")
def __str__(self):
return self.title
class ArticleImages(models.Model):
article = models.ForeignKey(Article, on_delete="models.CASCADE", related_name="image")
image = models.ImageField("image")
name = models.CharField(max_length=50, blank=True)
But so far I wasnt able to access my images in my template using
{{ article.image.url }} or {{ article.image.image.url }}
or any other combination. Why is that ?
Did I set up my models correctly ? One person suggested that I should change the model field from ForeignKey to OneToOneField, but I didn't get much feedback on why and how ?
So, how would I make a for loop that loops through the Articles model and then gets the related images for each Article ? I essentially want it to behave like I still have the 6 different fields like I did before. ( I have to do it this way, it's a part of the task ).
here are my views and my "index" template that I used to loop through the Articles and display 6 latest news on my home page. (please ignore the tags,I am aware they aren't working like this..the template is just so you can understand what I am talking about )
my views.py:
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latest_article_list'
def get_queryset(self):
return Article.objects.all()
class CategoryView(generic.ListView):
template_name = 'news/categories.html'
context_object_name = 'category'
def get_queryset(self):
return Article.objects.filter(article_category__category_title="Politics")
class ArticlesView(generic.ListView):
context_object_name = 'latest_article_list'
template_name = 'news/articles.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
def get_queryset(self):
category_pk = self.request.GET.get('pk', None)
if category_pk:
return Article.objects.filter(article_category__pk=category_pk).order_by("-pub_date")
return Article.objects.order_by("-pub_date")
def article(request, article_id):
article = get_object_or_404(Article, pk=article_id)
context = {'article': article,
'article_category': article.article_category.category_title}
return render(request, 'news/article.html', context)
template that I used with my old model :
{% for article in latest_article_list %}
<img class="single-article-img" src="{{ article.image.name.url }}" alt="">
<div class="container row">
<!-- Start Left Blog -->
<div class="article mt-10 col-md-4 col-sm-4 col-xs-12">
<div class="single-blog" style="margin:10px auto;">
<div class="single-blog-img">
<a href="{% url 'news:article' article.id %}#article-title">
<img class="for-imgs" src="{{ article.image.url }}" alt="">
</a>
</div>
<div class="blog-meta">
<span class="date-type">
<i class="fa fa-calendar"></i>{{ article.pub_date }}
</span>
</div>
<div class="xx blog-text">
<h4>
{{ article.title }}
</h4>
<p>
{{ article.description|truncatewords:30 }}
</p>
</div>
<span>
Read more
</span>
</div>
</div>
{% endfor %}
Thank you !
You need to loop over the images as you have many images against a single article object. You can have something like below to show images in your template:
{% if latest_article_list.articleimages %}
{% for articleimage in latest_article_list.articleimages.all %}
<img src="{{ articleimage.image.url }}" class="d-block w-100" alt="...">
{% endfor %}
{% endif %}

When published django-cms plugin from edit mode html template doesn't render model objects. But content reappears when switched to edit mode

Actually, I am new to Django-cms world. I tried almost every duplicate of this same question but none of them worked. I have created my custom plugin with just one ManyToManyField().
I went through Django-docs For many-to-many or foreign key relations to other objects but that didn't helped. Maybe I am lost somewhere, I would really appreciate any help. Thank you.
Try1 PluginModel :
class PortfolioPluginModel(CMSPlugin):
portfolio = models.ManyToManyField(Portfolio)
Try2 PluginModel :
class PortfolioPluginModel(CMSPlugin):
portfolio = models.ManyToManyField(Portfolio)
def copy_relations(self, oldinstance):
for p in oldinstance.portfolio.all():
p.pk = None
p.plugin = self
p.save()
Try3 PluginModel :
class PortfolioPluginModel(CMSPlugin):
portfolio = models.ManyToManyField(Portfolio)
def copy_relations(self, oldinstance):
self.portfolios = oldinstance.portfolios.all()
Apps Model:
class Portfolio(models.Model):
author = models.CharField(max_length = 100)
description = models.TextField()
image = models.ImageField(upload_to ='portfolioImage',blank = True, null = True)
published_at = models.DateTimeField(auto_now_add = True)
cms_plugins.py
#plugin_pool.register_plugin # register the plugin
class PortfolioPluginPublisher(CMSPluginBase):
model = PortfolioPluginModel # model where plugin data are saved
# model = Portfolio(CMSPlugin)
module = _("Portfolio")
name = _("Portfolio Plugin") # name of the plugin in the interface
render_template = "portfolio_cms_integration/portfolio_plugin.html"
cache = False
def render(self, context, instance, placeholder):
context.update({'instance': instance})
return context
portfolio_plugin.html
<div class="filters-content">
<div class="row grid">
{% for p in instance.portfolio.all %}
<div class="single-portfolio col-sm-4 all vector">
<div class="relative">
<div class="thumb">
<div class="overlay overlay-bg"></div>
<img class="image img-fluid" src="{{ p.image.url }}" alt="" style="width:399px; height: 400px;">
</div>
<a href="{{ p.image.url }}" class="img-pop-up">
<div class="middle">
<div class="text align-self-center d-flex"><img src="{% static 'img/preview.png' %}"
alt=""></div>
</div>
</a>
</div>
<div class="p-inner">
<h4>{{ p.author }}</h4>
<div class="cat">{{ p.description }}</div>
</div>
</div>
{% endfor %}
</div>
Expected output : When I publish my post I should see my model objects of portfolio app
Actual output: When I publish my post I dont see any model objects of portfolio app
Your last try works perfectly, you just need to delete the plugin and add it again. It should make use of the copy_relations function this time. You should then see your plugin inside the published view.
Don't forget copy_relations if you have ForeignKeys or ManyToManyField inside your CMSPlugin model.
Fore more information checkout Django CMS documentation about handling relations.

Django QuerySet in template for relational models

I am trying to setup a relation with another model using FK in django but I can't call FK related model fields in django templates. In template when I call "provider.name" it shows me "None" as result. I do have 5 different suppliers listed and can see from admin. I have providers and I got products like below:
class Product(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=20, decimal_places=2)
sku = models.CharField(null=True, max_length=100)
url = models.URLField(blank=True)
slug = models.SlugField(unique=True)
providers = models.ManyToManyField('Provider', blank=True)
def __unicode__(self):
return self.title
class Provider(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
logo = models.ImageField(upload_to='products/')
shipping_method = models.CharField(max_length=250)
shipping_time = models.CharField(max_length=250)
return_policy = models.CharField(max_length=200)
free_delivery = models.CharField(max_length=200)
customer_service_number = models.CharField(max_length=200)
shipping_charges = models.CharField(max_length=200)
def __unicode__(self):
return self.name
In template the way I am calling it is:
{% with provider=object.providers %}
{{ provider.name }}
{% endwith %}
As result it shows "None". I am trying to call provider in a different template. In it's own List/Detail generic views it works just fine. I am trying to call it in different app templates. Please advise.
Update................
My search.html for whoosh search engine includes
{% for result in page.object_list %}
<div class="col-lg-1-5 col-md-3 col-sm-4 col-xs-12">
<div class="pbox">
<div class="photo">
<img src="{{ result.object.get_image_url }}" alt="product">
</div>
<div class="info">
<div class="url">
name
{% for item in result.object.providers.all %}
{{ result.provider.name }}
{% endfor %}
</div>
<div class="description">
{{ result.object.description|truncatewords:7 }}
</div>
<div class="price">
{{ result.object.price|intcomma }}
</div>
</div>
</div>
</div>
As I am not using any ModelManager or just using default Managers pre-defined for models. I've added a line of code under Product App model like this:
def get_provider(self):
item = self.providers.all()
return item
I case of calling provider you call related manager so it return None because manager have no method like this.
If you want to retrieve objects from managers, you should call self.providers.all()
In your case, template tags should looks like this:
{% for item in object.providers.all %}
{{ provider.name }}
{% endfor %}

Categories