Showing extended page attributes from reverse_id - django

I've made an extra image field as shown by #Timmy O'Mahony on
How to add some extra fields to the page in django-cms? (in django admin panel)
I'd like to call this image field from another page using its reverse_id, much like
{% page_attribute "extended_page_options.image" "my_page_reverse_id" %}
Do I need a custom template tag? How can I do it?
I am using django-cms 2.4.3 and django 1.5.5
EDIT:
models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from cms.models.pagemodel import Page
from filer.fields.image import FilerImageField
class ExtendedPage(models.Model):
page = models.ForeignKey(Page, unique=True, verbose_name=_("Page"), editable=False, related_name='extended_fields')
image = FilerImageField(blank=True, null=True)

In a recent project, I think I solved this with a context_processor as follows:
from django.conf import settings
def page_extension(request):
"""Puts settings.DEBUG and the page_extension, if present and puts it into
the context"""
context = {'debug': settings.DEBUG}
if request.current_page:
page = request.current_page
try:
context.update({'page_extension': page.page_extension})
except:
try:
context.update({'page_extension': page.publisher_public.page_extension})
except:
pass
return context
In this case, I've also used the opportunity to also add the value of settings.DEBUG, this is optional of course.
Then, just add this to your settings file as:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'path.to.above.code.page_extension',
)
Then, next step, is just access the value in your templates:
{% if page_extension and page_extension.my_page_property %}{{ page_extension.my_page_property }}{% endif %}

First thing, if you are starting out the new project, go for django cms 3.0, it has lots of new and exciting features than the previous release.
Back to your question, go to the advanced settings of the page which contains the actual image field and add the id. Then on other page do like this:
{% page_attribute "field_name" "my_page_reverse_id" %}

Related

Django Autocomplete Light not working with Django update from Django 3.1 to 4.1

The application I inherited was at Django 2.2. I have gradually updated to 4.1 and everything works except the Django Autocomplete Light fields. For some forms.ModelForm, I have a box with a correct list that can be selected but it does not have the ability to type the first few letters and select from that filtered list. Most of the autocomplete fields are on forms.Form and either display a dropdown box where the only choice is '-----', a scroll bar only with no list, or a box that is 2 x 47 pixels.
I have gone through the DAL documentation but could not find a solution. I have searched here plus other Google searches but I have not found a solution.
I do realize that parts of the code use the older URL pattern that includes ^, $, and ?P< items. I have tried to change these for one of the apps but it did not solve the issue.
forms.py
# Forms
from django import forms
from django.forms import ModelForm
# Models
from login.models import User
from .models import Suggestion, SuggestionDecision, Implementation
# Autocomplete
from dal import autocomplete
class ReviewForm(forms.ModelForm):
class Meta:
model = Suggestion
fields = ['suggestion', 'supervisors', 'managers']
widgets = {
'supervisors': autocomplete.ModelSelect2Multiple(url='login:user_autocomplete'),
'managers': autocomplete.ModelSelect2Multiple(url='login:user_autocomplete')
}
urls.py
from django.urls import path, re_path
from process_improvement import views as pi_views
app_name='process_improvement'
re_path(r'^autocomplete/suggestion/$', pi_views.SuggestionAutocomplete.as_view(), name='suggestion_autocomplete')
views.py
from dal import autocomplete
from django.urls import reverse
from django.shortcuts import redirect
from django.contrib.auth.mixins import LoginRequiredMixin
class SuggestionAutocomplete(autocomplete.Select2QuerySetView, LoginRequiredMixin):
def get_queryset(self):
qs = Suggestion.objects.all()
if self.q:
for term in self.q.split():
qs = qs.filter(Q(suggestion__icontains=term) | Q(pk__icontains=term) | Q(created_at__icontains=term))
return qs
suggestion.html
<form method="POST" class="needs-validation" id="suggestionForm" data-parsley-validate>
{% csrf_token %}
<label for="id_supervisors">Supervisors*</label>
{{ suggestionForm.supervisors }}
{{ suggestionForm.suggestion | as_crispy_field }}
</form>
This is the code for the fields that appear with a list that can be selected but not the option to enter the first few letters of the name. What do I need to change to get this working properly?

Django : Trying to display the object from django admin to a HTML page

I'm trying to display text in a HTML page which i'm saving in django admin interface and whenever i save the data and trying to display the text in html page instead of the text a line space was being printed.
Output attaching the code screenshots here views file, HTML
page, Models, Django Admin Interface
views.py
from django.shortcuts import render
from .models import todolist
def index(request):
todo=todolist.objects.order_by('id')
context={'todo': todo}
return render(request,'todotemplates/index.html',context)
models.py
from django.db import models
# Create your models here.
class todolist(models.Model):
text=models.CharField(max_length=50)
complete=models.BooleanField(default=False)
def __str__(self):
return self.text
In your html use need to use i inside loop not todo:
{% for i in todo %}
<li class='whatever'>{{ i.text }}</li> <!-- use i.text -->
{% endfor %}
Please change your view code to below:
def index(request):
todohtml = ""
defectsobj1 = PrdDefect.objects.all()
for eltdefect1 in defectsobj1:
todohtml = "<li>"+eltdefect1.text+"</li>"
return render(request, "page.html", {"todohtml":todohtml})
Also can you check you database if you model is saving entries properly in db

Django parler TranslatableSlugMixin translates from English to another language, but once translated cannot be translated back returns 404

I'm using Django CMS with Django Parler and have run into a problem that is driving me mad, so if anybody could help, it would be much appreciated!
So I'm creating a simple blog app that has the slug as a translatable field. Here is the model simplified:
from parler.models import TranslatableModel, TranslatedFields
class Article(TranslatableModel):
...
translations = TranslatedFields(
...
slug = models.SlugField(_('slug'), max_length=255, blank=True, allow_unicode=True),
meta = {'unique_together': (('language_code', 'slug'),)}
)
...
def get_absolute_url(self):
return reverse('blog:article_detail', kwargs={'slug': self.slug})
Here are the urls:
from django.conf.urls import include, url
from .views import ArticleDetailView
urlpatterns = [
...
url(r'^(?P<slug>\w[-\w]*)/$', ArticleDetailView.as_view(), name='article_detail'),
]
And finally here is the view:
from django.views.generic import DetailView
from parler.views import TranslatableSlugMixin
from .models import Article
class ArticleDetailView(TranslatableSlugMixin, DetailView):
model = Article
template_name = 'blog/_article.html'
I've created an article that is in English, French & German, with a different slug for each language, lets call those:
/en/blog/english-slug
/fr/blog/french-slug
/de/blog/german-slug
I can navigate to these all correctly, but in Django CMS you have the language menu at the top that on the English page shows the links as:
/en/blog/english-slug
/fr/blog/english-slug
/de/blog/english-slug
This is fine, as that's what the TranslatableSlugMixin in the view handles (see here http://django-parler.readthedocs.io/en/latest/api/parler.views.html).
So when I click one of the links (say the French one) the view correctly finds the correct article and redirects me to the correct url. So clicking:
/fr/blog/english-slug
Has taken me correctly to:
/fr/blog/french-slug
But here's where it's all going wrong. I now want to navigate back to the English page, which is showing as:
/en/blog/french-slug
But when I click the link it navigates to a 404. This is the same if I navigate to the German URL from the French one. However if I go from English to German straight away it works.
Sorry, I know this is confusing to explain but it seems the translation works one way from base/default to other language but doesn't work correctly when swapping between languages or back to the base/default.
Surely TranslatableSlugMixin is designed to allow this to happen?! So am I missing something here?
Any help would be much appreciated. Happy to provide more info if necessary.
Thanks
Ok so I've figured out how to make this work and it turns out it's a combination of things...
Using the default Django CMS chooser was a mistake:
{% language_chooser "menu/language_chooser.html" %}
This leads to the URLs I described above:
/en/blog/english-slug
/fr/blog/english-slug
/de/blog/english-slug
Reading the Django Parler docs led me to using their language navigation menu:
{% for lang_code, title in LANGUAGES %}
{% get_language_info for lang_code as lang %}
{% get_translated_url lang_code as tr_url %}
{% if tr_url %}<li{% if lang_code == LANGUAGE_CODE %} class="is-selected"{% endif %}>{{ lang.name_local|capfirst }}</li>{% endif %}
{% endfor %}
This leads to the urls pointing to the correct location:
/en/blog/english-slug
/fr/blog/french-slug
/de/blog/german-slug
For the Django Parler navigation to work I needed to update the get_absolute_url() in the model to handle different languages. I did that as follows:
from django.utils.translation import get_language
from parler.models import TranslatableModel, TranslatedFields
class Article(TranslatableModel):
...
def get_absolute_url(self):
language = get_language()
if self.has_translation(language):
slug = self.safe_translation_getter('slug', language_code=language)
return reverse('blog:article_detail', kwargs={'slug': slug})
# no translation so fallback to all article list
return reverse('blog:article_list')
Phew! That was a headache! Hopefully this helps somebody else in the future!
P.S. During my research I came across this blog app that seems really great:
https://github.com/nephila/djangocms-blog
It helped me get to the bottom of this nightmare!
UPDATE
Looking at the get_absolute_url() in djangocms-blog (link above), they have a far better solution to the problem. Their implementation is:
from django.utils.translation import get_language
from parler.models import TranslatableModel, TranslatedFields
class Article(TranslatableModel):
...
def get_absolute_url(self, lang=None):
if not lang or lang not in self.get_available_languages():
lang = self.get_current_language()
if not lang or lang not in self.get_available_languages():
lang = get_language()
with switch_language(self, lang):
slug = self.safe_translation_getter('slug', language_code=lang, any_language=True)
return reverse('blog:article_detail', kwargs={'slug': slug})
Thanks nephila, this has saved me from a lot of cursing and frustration :)

Django: I can't succeed displaying multiple checkboxes in my template

I read the following thread: Django Multiple Choice Field / Checkbox Select Multiple
But I somehow miss something important as I can't succeed in displaying the checkboxes in my template. However, the name of the field does appear in the template but that's all, after the field name, it's all white and blank.
Curiously, in the thread I read, the author didn't wrote a list of tuple. That's why I think the problem could lie in the models.py
Here is my models.py
from django.db import models
from user.models import User
class RegionChoices(models.Model):
REGION_CHOICES = (
('London', 'Londres'),
('Paris', 'Paris'),
('Berlin', 'Berlin'),
)
region = models.CharField(max_length=30, choices=REGION_CHOICES)
def __str__(self):
return self.region
class Offer(models.Model):
publisher = models.ForeignKey(User)
content = models.TextField()
region_choices = models.ManyToManyField(RegionChoices)
def __str__(self):
return self.publisher.username
forms.py
from django import forms
from django.contrib import admin
from django.conf import settings
from offers.models import Offer, RegionChoices
class SendOfferForm(forms.ModelForm):
region_choices = forms.ModelMultipleChoiceField(queryset=RegionChoices.objects.all(), widget=forms.CheckboxSelectMultiple)
class Meta:
model = Offer
exclude = ['publisher']
offer.html
<form action="{% url "send_offer" %}" method='POST' class='sendofferform'>
{{ form.errors }}
{{ form.non_field_errors }}
{% csrf_token %}
{{ offerform.as_p }}
</form>
views.py
if offerform.is_valid():
sent = True
offer = offerform.save(commit=False)
offer.publisher = User.objects.get(id=logged_user.id)
offer.save()
offerform.save_m2m()
else:
print(offerform.errors)
From your code sounds like you want to limit the choices of region your project can have. I think you should create an admin for RegionChoices first. In there you could create entrances of RegionChoices you like. Follow the django docs if you are not sure how to create an admin interface for a model https://docs.djangoproject.com/en/1.8/ref/contrib/admin/
ps: You might want to do unique=True on region field in RegionChoices. Otherwise you might create duplicate entries of the same region by accident.
Okay, I realize I had to load data in the model RegionChoices.
I loaded the data in the admin part of my website and now, it works perfectly.

How to create an article list view in django-cms

I am an absolute beginner in django-cms, just acquired some pieces of knowledge to create templates. Just wondering, how to create a portal page that has a few acticles in each different categories?
Please simply point out a practical way to do, no real code is needed.
Thank you.
As others have pointed out, the way to do this is by hooking your CMS page to another set of views. Django-CMS provides application hooks:
#cms_app.py
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
class YourModuleApp(CMSApp):
name = 'My App MOdule'
urls = ['my_app.urls']
apphook_pool.register(YourModuleApp)
So, if you had a module called "my_app" with a urls.py in it, Django-CMS will add those patterns to the page. Look in the "Advanced Settings" section of the page in admin for the application drop-down menu.
Once the app is hooked to the page, Django-CMS will pull any content and the layout template from the information it holds, then hand off processing to the additional URL patterns that are hooked to it. That's how you can pull in another model, add a form, handle a POST, etc.
You could just do it the normal Django way. Create a normal Django app, with a URL pointing to a view that renders a template. The view could look like this:
from django.shortcuts import render
from cms.models import Page
def articles(request):
pages = Page.objects.public()
render(request, 'example.html', {'pages': pages})
And the template could look like this:
{% load cms_tags %}
{% for page in pages %}
<p>{% page_attribute "page_title" page %}</p>
{% endfor %}
You could stop here. Or you could have...
Additional Django CMS integration with AppHooks
Do you want your non-developer content managers to be able to put a list of articles wherever they want? This is where AppHooks come in.
Create a CMSApp class in the file appname/cms_app.py like this:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
class ArticleListApp(CMSApp):
app_name = 'articlelist'
name = _('Article List')
def get_urls(self, page=None, language=None, **kwargs):
return ['articlelist.urls']
apphook_pool.register(YourModuleApp)
Delete the URL entry in your project-wide urls.py as you no longer need it. Your app's urls.py needs to include a view for the regex ^$.
Now you or any content manager user with necessary permissions can create a page in the admin interface, and modify the advanced settings to select the "Article List" application:
One gotcha is that this will have no effect until the page is published (as well as all of its ancestor pages).