Django-cms and autoblocks - django

I am working with online shop project. In product model, i have description field but it's empty. Somehow description data is stored in django-cms and autoblocks. From page, i can edit that description with django-cms. In template tag {% autoblock product.slug %} line is description.
In views.py, i have 'product' passed as context, but it's slug field has nothing to do with description. Also, if i write {{ product.slug }}, it gives me slug.
Also googled about Autoblocks, but what managed to find out it's this model:
class Autoblock(models.Model):
composite_id = models.CharField(max_length=150)
content = PlaceholderField('content')
site = models.ForeignKey(Site)
def __unicode__(self):
return self.composite_id
All of these fields has nothing to do with description.
Im struggling for long time to find where that description is. Any hints would be appreciated.
EDITED:
Product model:
class HBaseProduct(Product):
def __init__(self, *args, **kwargs):
super(HBaseProduct, self).__init__(*args, **kwargs)
image = models.ImageField(upload_to='images/', blank=True, max_length=300, verbose_name=_('Image'))
position = models.IntegerField(default=0, null=False)
description = models.TextField(null=True, blank=True)

Your description is in Autoblock.content, which is a ForeignKey to 'cms.Placeholder', which in turn holds a tree of 'cms.CMSPlugin' models in cmsplugin_set.
There's currently no straight-forward way to turn a Placeholder into a string (of HTML) to be used somewhere else outside of a request/response cycle.
Your best bet is to call Placeholder.render with a context object that holds a (fake) Django Request object. This will return the rendered contents and you can then store that in description.

Related

Getting a TypeError while trying to make a unique page in Django

I'm making a picture gallery web-app. I want to make some of displayed photos to belong to a specific collection. Each collection is supposed to have its own page that displays all of the pictures that belong to it.
The name of each unique page is supposed to be photo_collection model, which I added to the class Image in models.py. But for some reason, I get TypeError at /projects/sample_collection_name/ - photo_collection() got an unexpected keyword argument 'photo_collection'
No idea what's causing this error. I tried renaming the photo_collection function (it has the same name as my model), but it didn't work.
models.py
class Image(models.Model):
title = models.CharField(max_length=300)
image = models.ImageField(null=True, blank=True, upload_to='images/')
pub_date = models.DateTimeField('Date published', default=timezone.now)
photo_collection = models.CharField('Photo collection', max_length=250, null=True, blank=True)
views.py
def photo_collection(request):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
urls.py
urlpatterns = [
#some other patterns here
path('projects/<str:photo_collection>/', views.photo_collection, name='photo_collection'),
]
gallery.html
{% if i.photo_collection != Null %}
{{i.photo_collection}}
{% endif %}
you need to add photo_collection to your view parameters.
it will be like this:
def photo_collection(request, photo_collection):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
when ever you add a variable to your url path you need to add that variable to view parameters too.
here is the documentation for this matter:
https://docs.djangoproject.com/en/4.1/topics/http/urls/

Can I have data from different fields of a model returned as separate HTML elements? (Django 2.1)

I am compiling a database of articles and have my model set up like this:
class articles(models.Model):
ArticleID = models.IntegerField(primary_key=True)
Title = models.CharField(max_length=500)
Author = models.CharField(max_length=200, null=True)
Journal = models.CharField(max_length=500, null=True)
Date = models.IntegerField(null=True)
Issue = models.IntegerField(null=True)
Link = models.URLField(max_length=800, null=True)
Content = models.TextField()
class Meta:
db_table = 'TEST'
def __str__(self):
return f'{self.Title}, {self.Author}, {self.Journal},{self.Date}, {self.Issue}, {self.Content}'
def get_absolute_url(self):
return reverse('article-detail', args=[str(self.ArticleID)])
The idea is pretty simple. Each meta data type (i.e. title, author) is it's own field, and the actual content of the article is in the field Content.
My view for this model:
def article_detail(request, ArticleID):
ArticleID = get_object_or_404(articles, ArticleID=ArticleID)
context = {'ArticleID': ArticleID}
return render(request, 'article_detail.html', context)
The HTML template for the view:
{% extends 'base.html' %}
{% block content %}
<div class="container">
{{ ArticleID }}
</div>
{% endblock %}
The data displayed in on the HTML page is one big block of text in one single HTML element. How can I make it so that I can use CSS to target each individual field from the model? Must I make separate models for each field (and bound them with foreign keys)?
No of course not. You can access fields with normal dot notation: ArticleID.Title, ArticleID.Author, etc.
(But you shouldn't call your context variable ArticleID; it's the whole article, not the ID. Also, Python style is to use lower_case_with_underscore for variables and attribute names.)
There is already a primary key in for every model which is called id, you don't have to explicitly declare that.
Secondly you are getting an article object with get_object_or_404 so if you use . (dot) notation you will get your desired value in your template.
Something like-
<h2>{{article.Title}}</h2>
<p>{{article.Content}}</p>
though you have to send article names instead of ArticleID in context variable.
In addition to Mr. Daniel Roseman's comment you should use class name Article instead of articles which is not pythonic.

django/wagtail model - if ForeignKey wagtailimages.Image field is saved blank, then save default

I am saving a model for a BlogPage(Page) and want to allow author to input image, but if they choose not to, automatically save a specific image.
Here is my model:
from django.db import models
from core.models import Page
from modelcluster.fields import ParentalKey, ParentalManyToManyField
from modelcluster.tags import ClusterTaggableManager
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
class BlogPage(Page):
date = models.DateField('Post Date')
categories = ParentalManyToManyField('blog.BlogCategory', blank=True)
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
Would the best way to set a ForeignKey on save() be by:
def save(self, *args, **kwargs):
if self.feed_image = None:
self.feed_image = something?
super(BlogPage, self).save(*args, **kwargs)
if so, what is the expected input of something? an image url?
Or is it cleaner to set a default on the ForeignKey, like:
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
default=something?
on_delete=models.SET_NULL,
related_name='+'
)
if so, what would is the expected format of that something??
I know this logic can be done in the template, but it interferes and if possible, I'd like it to be done in the model.
Thanks!
you can simply save None on your feed_image then create a get method that will return your default picture, like that :
if self.feed_image:
return self.feed_image
return something?
The something should be a wagtailimages.Image object.
self.feed_image = Image.objects.first()
But because the feed_image is a ForeignKey field, only an id is stored in the database. So this will work as well (assuming there is a object with that id):
self.feed_image_id = 1
Finally, I'd like to point out that you are mixing code and content! That is always a bad idea. What if someone deletes your default image? It is better to drop the custom save method, leave the image field empty and use a file from your static folder. Do something like this in the template:
{% if object.feed_image %}
// Your image tag here.
{% else %}
<img src={% static 'images/default.jpg' %}>
{% endif %}

Django Admin showing thumbnail of foreign key's imagefield in inline form

I have a simple structure, with a Product model, and AppMedia model and a ProductMedia join table.
Product (has many) ProductMedia
ProductMedia (has one) AppMedia
What I would really like is to see the thumbnail for AppMedia's media field in an inline form in Product.
e.g. edit Product in Django admin, shows a StackedInline form. This contains (at present) a drop down (select) of all AppMedia.
What I need is the thumbnail.
Any help appreciated.
I am certain this isn't difficult, but I'm struggling. It's quite straight forward to put a thumbnail in for the AppMedia form (which is where the media ImageField resides), but not for the ProductMedia form which is using AppMedia as a ForeignKey.
Basic Models...
class Product(models.Model):
name = models.CharField(max_length=100)
class AppMedia(models.Model):
media = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
media = models.ForeignKey(AppMedia)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product)
The AppMedia is shared in this way to stop multiple uploads of the same file and also to store extra meta-data (not displayed here) with the image.
I had the same issue. I hope at least one of my attempts will be usable for your case:
My first attempt to solve this problem (without overriding ForeignKeyRawIdWidget) was to:
set form property in inline admin where thumbnail should be displayed
add another field into given form class with widget, which will display a thumbnail
However I abandoned this solution because I think I have to inject data about thumbnail into given field in form's constructor and I don't consider it a nice solution.
My next solution was to use MultiWidget for some field. In this case I don't need to add another field to form and I'll have data required to display thumbnail in widget without any need to inject them in constructor.
class ThumbnailMultiWidget(MultiWidget):
def decompress(self, value):
#has to be overriden
return [None,None]
class ThumbnailWidget(Widget):
def render(self, name, value, attrs=None):
#not quite sure what is in `value`, I've not been so far
return '<img src="%s"/>' % value.url
class PhotoInlineForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PhotoInlineForm, self).__init__(*args, **kwargs)
wdgts = [self.fields['media'].widget, ThumbnailWidget()]
self.fields['media'].widget = ThumbnailMultiWidget(widgets=wdgts)
class Meta:
model = RecipePhoto
But I abandoned this solution as well, because I found out, that there's actually an instance's representation in ForeignKeyRawIdWidget (which is widget I use) with all data I need to show the thumbnail. And that is my final solution:
So because my inline items have raw_id_field for choosing an inline record, I could simply override method label_for_value in ForeignKeyRawIdWidget, which is used to represent existing inline record. Usually it's __unicode__ (I think). I inherited ForeignKeyRawIdWidget and overriden this method to show image thumbnail:
class PhotoForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
def label_for_value(self, value):
key = self.rel.get_related_field().name
try:
obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
except (ValueError, self.rel.to.DoesNotExist):
return ''
else:
"""
there's utilized sorl.thumbnail, but you can return st like this:
<img src='%s' /> % obj.media.url
"""
return Template("""{% load thumbnail %}
{% thumbnail image.image "120x120" crop="center" as one %}
<img src="{{ one.url }}" />
{% endthumbnail %}""").render(Context({
'image': obj
}))
class AppMediaInlineAdmin(admin.TabularInline):
model = AppMedia
extra = 1
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'media':
db = kwargs.get('using')
kwargs['widget'] = PhotoForeignKeyRawIdWidget(db_field.rel, self.admin_site, using=db)
return super(AppMediaInlineAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
You should look into the related_name parameter for foreign keys, which will allow you to access the ProductMedia in reverse, i.e. if you change the ProductMedia model to:
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
media = models.ForeignKey(AppMedia)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product, related_name='media')
You could access the media object in Product model, which will allow you to put it in your admin in-line form. i.e you would have (for an easier explanation I've put the ImageField in ProductMedia):
class Product(models.Model):
name = models.CharField(max_length=100)
def admin_image(self):
return '<img src="%s"/>' % (self.media.all()[0].image.url)
admin_image.allow_tags = True
class ProductMedia(models.Model):
title = models.CharField(max_length=150)
image = models.ImageField(upload_to=appmedia_upload_to) #appmedia_upload_to defined elsewhere)
media_order = models.IntegerField(default=0)
product = models.ForeignKey(Product, related_name='media')
Then in your admin.py put:
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'admin_image')
admin.site.register(models.Product, ProductAdmin)
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
Hope I understood your question correctly and that helped. Also the code isn't tested but I'm pretty sure it should work.

django many to many get attributes from other object

So I've been using django for a while now, and it's great. I've recently come across a little bit of a problem, and I'm sure there's a crappy way to get it to work, but what I've found with Django is that they've usually built in all sorts of mechanisms to do things for you. So what I'm not finding is this:
Here are my models:
class LandmarkGroup(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
IsActive = models.BooleanField(default=True)
landmarks = models.ManyToManyField('Landmark', blank=True, null=True)
def __unicode__(self):
return self.Name
class Landmark(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
Polygon = models.PolygonField()
IsActive = models.BooleanField(default=True)
objects = models.GeoManager()
def __unicode__(self):
return self.Name
I also have another model 'Team' that has a ManyToMany with LandmarkGroup, but I'm not going to post it here. I have a view where I query for all the landmarks that have a landmarkgroup that has a team with the same team id as the one I passed in:
def mobile_startup(request):
...
landmarkGroups = LandmarkGroup.objects.filter(team=device.team, IsActive=True)
landmarks = Landmark.objects.filter(landmarkgroup__team=device.team, IsActive=True)
...
return render_to_response('webservice/mobile_startup.html', {'landmarks': landmarks, 'landmarkGroups': landmarkGroups})
Everything works, the only problem I'm having is, I'm returning this all as JSON to the mobile app, and I want to provide the landmarkGroup id for the landmark, so in my template I've been trying to:
"landmarkGroup" : {{ landmark.landmarkgroup.id }} }
but that's not working. Does anyone know any way I can get the landmarkGroup ID for each landmark in my set? Do I need to extract it when I do the query? I know I can reference each landmarkGroup in the query because I can do 'landmarkgroup__team=device.team', but I need to able to reference this object in the template
LandmarkGroup.landmarksis a ManyToManyField,therefore one Landmark can belong to multiple groups.
You should be able to output them in your template like this:
{% for group in landmark.landmarkgroup_set.all %}{{ group.pk }}{% endfor %}
The first group belonging to the landmark should be accessible through {% landmark.landmarkgroup_set.all.0 %}