I've been trying to get syntax highlighting working in my simple Django (1.1) test app using Markdown (2.0.1) & Pygments (1.0). The idea is to generate HTML from the users input which is in markdown format and store both in the DB so I don't have to do the markdown to html translation during the fetch.
So far I have the markdown processing working but I cannot seem to get syntax highlighting working. My models.py looks like this:
from django.db import models
from django.contrib import admin
from markdown import markdown
class BlogPost( models.Model ):
title = models.CharField( max_length = 150 )
body = models.TextField()
body_html = models.TextField(editable=False, blank=True, null=True)
timestamp = models.DateTimeField()
def save(self):
self.body_html = markdown(self.body, ['codehilite'])
super( BlogPost, self).save()
class Meta:
ordering = ( '-timestamp', )
class BlogPostAdmin( admin.ModelAdmin ):
list_display = ( 'title', 'timestamp' )
admin.site.register(BlogPost, BlogPostAdmin)
So far testing just markdown syntax works but if I try something like the following I don't seen any syntax highlighting in the output or the output source:
:::python
from foo import bar
foobar = bar('foo')
I'd expect to see at least a set of code elements in the output source.
Fixed it! The code should have been indented four spaces not three!
I made multiple edits to test that out before asking the question but it would seem Firefox cached the page as using as a test post. As I had been using the windows keyboard shortcut to force a page reload not the mac keyboard shortcut, d'oh!
I spotted it was working when I made a new test post out of frustration with four space indenting and then inspected the page source.
It's better to store it in the database in markdown format, and then convert it to the presentation format you'd like (HTML) at display time. That way you can edit your data the same way you added it in the first place.
At the top of your template you should include:
{% load markup %}
Then use the template filter markdown.
{{ blog_post.body|markdown}}
Then just use css to make sure you have the proper formatting.
You also need to install the markdown package if you don't have it here.
And in your settings.py in your INSTALLED_APPS you should include 'django.contrib.markup'
For more information see this page.
As for why you don't see formatting, check the marked up source and make sure it is working correctly. i.e. make sure it is marking up properly. Then make sure you have the needed stylesheets.
Markdown format is the format before it is marked up.
You can also use JQuery to add a class to the marked up elements, so you can style the markdown text without affecting the rest of the page.
Related
my goal is to filter if the name of an uploaded document contains a search (a simple string from an input field submitted by the user).
Strangely this works for long substring but not for short ones.
Example
My filter query is the following:
Tab.objects.filter(document__file__icontains=search)
where Tab is in simplest form a Model class like:
class Tab(models.Model):
name = models.CharField(max_length=50)
and Document is another Model class wit a relation to Tabs and a file field.
class Document(models.Model):
tab = models.ForeignKey(Tab, on_delete=models.CASCADE)
file = models.FileField(upload_to=custom_location)
Weird behavior
I've uploaded a file called example_test_123.pdf and if my search = example_test or test_123.pdf it results in a hit like expected.
However, if search = test the filter doesn't find the file.
Is this behaviour known or am I missing something here? Is it possible that the built-in name file is doing some trouble?
Edit
I changed file to document_file without success
Even _test or test_ is working
Edit #2
# Iain Shelvington (Comment)
This is no special function... only setting the subdirs the file should be saved
from django.utils.text import slugify
def custom_location(instance, filename):
return os.path.join('documents', slugify(instance.tab.name), filename)
But your question is pointing me in the right direction for a workaround (thanks):
I'm doing an annotation before filtering. companies is in this case a ManyToManyField to another model:
tabs = Tab.objects.all().annotate(num_companies=Count('companies'))
and then filtering for some stuff like
tabs = tabs.filter(num_companies__gte=3)
Removing these lines results in a correct filtering.
So I'm doing the annotations after filtering for the filename.
But I'm still curious what's happening here...
Thanks.
(Django 3.2.9 and Python 3.6.8)
I cannot find a replacement for the LinkColumn in the new versions of django-tables2. Author states that LinkColumn is deprecated and shouldn't be used. But the new linkify solution is poorly documented and doesn't have all the features of the old version. For example, I have this column:
edit = tables.LinkColumn(
'wagtailadmin_pages:edit', args=[A('page.pk')],
text='Edit'
)
It displays a link to the wagtail admin edit page called Edit. There's simply no way to achieve the same using linkify because linkify only works if you have valid accessor on the column. But accessor cannot return same static text for all rows (unless I modify the model to add a dummy property - but this particular model is in the 3rd party package and it would feel like a duct tape solution anyway).
In all other cases, column will not display a link. I've studied the source code and it seems that such case is simply not supported by the django-tables2 > 2.0.0.
Is there any clean and understandable way to construct a link column with a static link text using linkify?
Answering my own question. It seems that it is impossible to fully replace LinkColumn with the linkify feature. The following code solves my problem:
from django.urls import reverse
from django.utils.text import mark_safe
import django_tables2 as tables
from wagtail.core.models import PageRevision
class WagtailRevisionsTable(tables.Table):
title = tables.Column(
accessor='page.title',
linkify=lambda record: record.page.url,
verbose_name='Title'
)
edit = tables.Column(
accessor='page.pk'
)
class Meta:
model = PageRevision
fields = ('title', 'created_at', 'user', 'edit')
template_name = 'django_tables2/bootstrap-responsive.html'
def render_edit(self, value):
url = reverse('wagtailadmin_pages:edit', args=[value])
return mark_safe(f'Edit')
The code with the old LinkColumn was much more concise, I don't understand the reason for change and documentations really doesn't help. There's simply not enough information on linkify or render_col methods.
So I hope this answer will help some poor soul trying to port old code to the django-tables2 >= 2.0.
I recently added a new model to my site, and I'm using an admin.py file to specify exactly how I want it to appear in the admin site. It works great, but I can't figure out how to get one of my date fields to include seconds in it's display format. I'm only seeing values like "Aug. 27, 2011, 12:12 p.m." when what I want to be seeing is "Aug. 27, 2011, 12:12*:37* p.m."
Try this in the ModelAdmin:
def time_seconds(self, obj):
return obj.timefield.strftime("%d %b %Y %H:%M:%S")
time_seconds.admin_order_field = 'timefield'
time_seconds.short_description = 'Precise Time'
list_display = ('id', 'time_seconds', )
Replacing "timefield" with the appropriate field in your model, of course, and adding any other needed fields in "list_display".
digging around I ended here but applied a different approach to my app.
Changing django admin default formats could be done changing the django locale formats for every type you want.
Put the following on your admin.py file (or settings.py) to change datetime default format at your django admin.
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT = "d M Y H:i:s"
It will change the ModelAdmin's datetime formats on that file (or whole site if in settings).
It does not breaks admin datetime filters and order features as #Alan Illing has point out in comments .
hope this help in future
Extra info:
You can change it for every available locale in django, which are a lot.
You can change the following formats using this approach
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT
es_formats.NUMBER_GROUPING
es_formats.DATETIME_INPUT_FORMATS
es_formats.SHORT_DATETIME_FORMAT
es_formats.DATE_FORMAT
es_formats.SHORT_DATE_FORMAT
es_formats.DATE_INPUT_FORMATS
es_formats.THOUSAND_SEPARATOR
es_formats.DECIMAL_SEPARATOR
es_formats.TIME_FORMAT
es_formats.FIRST_DAY_OF_WEEK
es_formats.YEAR_MONTH_FORMAT
es_formats.MONTH_DAY_FORMAT
If you've tried gabriel's answer but it did not work, try to set USE_L10N = False in settings.py, it works for me.
Note that if USE_L10N is set to True, then the locale-dictated format has higher precedence and will be applied instead
See: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-DATETIME_FORMAT
The accepted answer is correct, however I found it a bit confusing to understand how/why it works. Below is a small example that I hope illustrates how to do this more clearly.
Django provides a few ways to display "custom" fields in your admin view. The way I prefer to achieve this behavior is to define a custom field in the ModelAdmin class and display that instead of your intended field:
from django.contrib import admin
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
class PersonAdmin(admin.ModelAdmin):
#admin.display(description='Birthday')
def admin_birthday(self, obj):
return obj.birthday.strftime('%Y-%m-%d')
list_display = ('name', 'admin_birthday')
Notice that instead of displaying the actual birthday field from the Person model, we define a custom field (admin_birthday) as a method in the PersonAdmin and display that instead by adding it to the list_display attribute. Furthermore, the admin.display() decorator modifies how Django will display this custom field in the admin view. Using this approach, the admin panel will show the NAME and BIRTHDAY fields but using your preferred date formatting for the date.
The reason I prefer this approach is you keep the Model field definitions separate from how you display them in the admin panel. You can read more about alternative approaches in the Django admin documentation.
I've the following problem:
In my models.py I'm using a Wagtail Image like this:
class ArtWork(models.Model):
image = models.ForeignKey(
'wagtailimages.Image',
on_delete=models.CASCADE,
related_name='+'
)
...
...
...
I know i can use the Image in my templates now like this:
{% image artwork.image height-100 %}
But for my current project I need to use the Image tag in my views.py because I'd like to generate a Pdf with it.
by simple using artwork.image just the Title of the Image returns but I'd need something like:
<img alt="SomeName" src="/media/images/SomeName_155x200.max-1200x1200_22b3pND.height-100.jpg" width="77" height="100">
Also artwork.image.url gives no result.
How can I use Wagtail Images outside my templates?
If you need to get an URL of rendition (thumbnail), you can do the following:
artwork_page_object.image.get_rendition('height-100').url
or to access rendition file:
artwork_page_object.image.get_rendition('height-100').file
Instead of height-100 you can use any valid resizing method for the image template tag (see the documentation for the image tag)
If you want to access original image you can do it like:
artwork_page_object.image.file
Useful docs:
Wagtail: More control over page rendering
Wagtail: Generating renditions in Python (Thanks, #shacker)
Django: ImageField and FileField
Template tags are just functions you can find the code on github. The image tag relies on the models Filter and Rendition.
I would recommend to read that code to understand how it works and afterwards use it in your view.
As noted by user Shacker here are the docs: https://docs.wagtail.org/en/stable/advanced_topics/images/renditions.html#image-renditions
Example:
# in your models
from wagtail.core.models import Page
class MyCustomPageModel(Page):
photo = models.ForeignKey(
'wagtailimages.Image',
...
# in your view
from myapp.models import MyCustomPageModel
p = MyCustomPageModel.objects.all()[0]
r = p.photo.get_rendition('fill-300x150')
print(r.url)
I'm using an inline admin in my Django application. I want to have some help text displayed in the admin form for Page to go with the inline admin (not just the individual help text for each field within that model). I've been trying to figure out how to do this, but cannot seem to find anything on the issue. Am I missing some trivial out-of-the box option for doing this?
If there's no super simple way to do this, is there a way to do this by extending some template?
Below are parts of my models and their admins:
class Page(models.Model):
....
class File(models.Model):
page = models.ForeignKey(Page)
....
class FileAdminInline(admin.TabularInline):
model = File
extra = 0
class PageAdmin(admin.ModelAdmin):
inlines = (FileAdminInline,)
If you're not talking about specific help_text attribute then then look at this post it shows an underdocumented way of accomplishing this.
If you don't want to mess around with getting the help_text information into the formset's context and modify the edit_inline template, there is a way of capturing the verbose_name_plural Meta attribute of your model for that purpose.
Basic idea: If you mark that string as safe you can insert any html element that comes to your mind. For example an image element with it's title set to global your model help text. This could look somethink like this:
class Meta:
verbose_name = "Ygritte"
verbose_name_plural = mark_safe('Ygrittes <img src="' + settings.STATIC_URL + \
'admin/img/icon-unknown.svg" class="help help-tooltip" '
'width="15" height="15" '
'title="You know nothing, Jon Snow"/>')
Of course - this is kind of hacky - but this works quite simple, if your model is only accessed as an inline model and you don't need the plural verbose name for other things (e.g. like in the list of models in your application's admin overview).