I am porting a python / Django application from:
Django==1.5.1
python version 2.6.6
to
Django==3.2
python version 3.6.8
The issue that I am having is that I have a section of code that renders to string, a particular HTML template, and then adds that to list to output elsewhere.
The actual code that produces this HTML is:
class AccountAdmin(SmarterModelAdmin):
list_display = ('username', 'files_url', 'teamMembers', 'roleMemberships', 'safetyLimit', 'admin_quota', 'manager', 'enabled', 'path')
list_filter = ['enabled', 'manager', 'accountType']
search_fields = ['username']
inlines = [RolesInline]
valid_lookups = (
'members__member__username',
'teams__role__username',
)
roles = Account.objects.filter(teams__member=account).order_by('username')
roleList = []
for role in roles:
link = '/admin/files/account/?teams__role__username=' + role.username
# mylink = ''+role.username+''
# linkText = format_html(mylink,myurl=link)
linkText = render_to_string('common/contact.html', context={'URL': link, 'contact': role})
roleList.append(linkText)
return ', '.join(roleList)
roleMemberships.short_description='Roles'
roleMemberships.allow_tags=True```
I have added in a logging.warn to validate what comes out of the render_to_string, and it is straight HTML.
The commented out lines were something that I tried that fixed a similar issue.
common/contact.html is:
<a
href="{{ URL }}"
{% if not contact.enabled %}
style="text-decoration:line-through;"
{% endif %}
>{{ contact.username }}</a>
However, on the final render, It comes out like this:
<a href="/admin/files/account/?teams__role__username=abaumann"
>abaumann</a>,
abaumann
which when run through a browser looks like this:
I haven’t been able to find anything that references this particular issue. I have everything else working in the admin section of the django application, and I am at a loss for why it is not rendering as expected.
Please let me know if there is anything else that you need to see.
I was on the right track, just had an issue with implementation.
Django backend tries to make everything secure. As such, the generated HTML from my template was not considered safe. So, using the mark_safe() function was the key. But where to place it was the challenge.
Anytime the variable containing the HTML is modified IN ANY WAY, the "safe" flag is removed, and the variable is passed through encoding to render any HTML as text.
So the fixe is, on this line:
return ', '.join(roleList)
change it to:
return mark_safe(', '.join(roleList))
My mistake was placing the mark_safe on the linkText variable either at the time the vaiable was set, or when it was appended to the roleList.
Related
The installation page looked simple enough. I installed it, added easy_select2 in INSTALLED_APPS in the settings, ran collectstatic, and then had this in my form:
from easy_select2 import Select2Multiple
# from django_select2 import forms as select2_forms
class LeadForm(forms.Form):
email = forms.CharField(max_length=100)
overseas_company = forms.MultipleChoiceField(
choices=countries,
label='Do you have any companies overseas and where?',
widget=Select2Multiple()
)
But it still renders as if I had done nothing at all. I tried django_select2 as well, and it didn't work either, so I must be doing something wrong Select2 wise.
I tried looking at the HTTP request log. Merely enabling easy_select2 doesn't make the template request the jQuery/select2 js files that are needed for the Select2 widget to function. Is this the problem? But the tutorial never said I had to add anything to any existing templates.
I had the same problem too.
You have to add {{ form.media }} in the head section of your template for django to make it work.
source: http://do-it-big.com/getting-django-easy-select2-to-include-jquery-and-friends/
Sorry in advance if there is an obvious answer to this, I'm still learning the ropes with Django.
I'm creating a website which has 6 pre determined subjects (not stored in DB)
english, civics, literature, language, history, bible
each subject is going to be associated with a unique color.
I've got a template for a subject.html page and a view that loads from the url appname/subject/subjectname
what I need to do is apply particular css to style the page according to the subject accessed. for example if the user goes to appname/subject/english I want the page to be "themed" to english.
I hope I've made myself clear, also I would like to know if there is a way I can add actual css code to the stylesheet and not have to change attributes one by one from the back-end.
thanks very much!
In templates you can use conditionals for add css, like this:
<div class="{% if subject=='civics' %}civic-class{% endif %}"></div>
For this, subject value should come from view.
Now, for themed page, you could use the extends tag. Let's supose:
def your_view(request):
subject # Here you get the url subject, 'how' is up to you
if subject == 'english'
template_base = '/some/html/tenplate.html'
elif subject == 'civis':
template_base = '/some/other/template.html'
... # then you return 'template_base' variable to template
Then in template:
{% extends template_base %} # at the top
Hope this helps, is the same logic if you use Class-Based views.
Django's views are not responsible for the presentation, it's the template (and css etc of course)'s reponsability. Now assuming you have the same view serving different subjects, the view obviously need to know which is the current subject (I assume from a captured part of the url passed as argument to the view), so it can easily pass this information to the template, which in turn can use it to add a subject-specific class to the body tag. Then you only have to write your css accordingly.
As an example:
# urls.py
patterns = urlpatterns('',
#...
url(r'whatever/(P?<subject>[a-z-]+>)/$', 'myviews.index', ...),
)
# myviews.py
def index(request, subject):
# do whatever
context = {
# whatever else
'subject':subject
}
return render(request, "whatever/index.html", context)
# whatever/index.html
<html>
# headers etc
<body class="something {{ subject }} etc">
# whatever here
</body>
</html>
You can do this is many ways.
In general you need to return some variable from your view to the html and depending on this variable select a style sheet, if your variable name will match you style sheet's name you can do "{{variable}}.css", if not you can use JQuery.
First, a little background: My healthcare office has a network drive on which we store all of our patient records. The default Windows Explorer interface does not provide enough data safety when accessing the network drive. Users are able to accidentally rename things, move things, name things improperly, etc. When this happens, it becomes very difficult to find records.
So, I am trying to fix that by designing an interface that is "safer." I decided to use Python and Django because - once the database is set up - it's actually much faster, and it would be nice to not have to install an application on every computer in the office - they just visit the internal web page. I've already designed the back end python code, which keeps the database synced with the network drive. It wasn't easy, but I was able to figure it out. However, now I have to design the front end, which lets the users update the database, and I'm not a web programmer, so the thought of learning advanced html, css, jquery, etc just for this one project was daunting. I also really like the way the Django Admin interface looked. Finally, it did mostly what I wanted it to do - provided an easy way to see what was there, and change that if necessary. So, I've decided to try and tweak the admin templates and such to get them to do what I want. There are a lot of issues I'm running into, but I've decided to make a separate question for each one, and this is the one I'm working on right now:
Each record is a pdf, and I'm looking at the InlineTab template and trying to get it to include a link to that pdf file on the local file system. I've got it to display a link, but when I click on it, nothing happens. Ideally, when I click on the link, I'd like it to show the pdf in a space set aside for that on the page (currently the information for each record goes across the whole screen, and I want it to only go partway across and have the rest of the screen show the pdf of the selected record), but for now I'd settle for getting the link to open on its own.
my models:
class Patient(models.Model):
"""Holds all the information needed for a Patient and their records"""
last_name = models.CharField(max_length = 100)
first_name = models.CharField(max_length = 100)
mid_init = models.CharField(max_length = 1, blank = True)
dob = models.CharField(max_length = 20, blank = True)
path = models.CharField(max_length = 500, blank = True)
class Record(models.Model):
"Holds all the information needed for a certain record"""
patient = models.ForeignKey(Patient)
title = models.CharField(max_length = 500)
created = models.CharField(max_length = 20, blank = True)
path = models.CharField(max_length = 200)
my "admin.py":
from django.contrib import admin
from records.models import Patient, Record
class RecordInline(admin.TabularInline):
model = Record
extra = 0
readonly_fields = ['path']
class PatientAdmin(admin.ModelAdmin):
fieldsets = [
('Patient Info', {'fields': ['last_name', 'first_name', 'mid_init', 'dob']}),
('Patient Folder', {'fields': ['path'], 'classes': ['collapse']}),
]
inlines = [RecordInline]
list_display = ('last_name', 'first_name', 'mid_init', 'dob')
search_fields = ('last_name', 'first_name', 'mid_init', 'dob')
admin.site.register(Patient, PatientAdmin)
You can see that the records are listed as a "tabular inline," so I tracked down that template (tabular.html), and this seems to be the relevant section of that template:
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
{% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %}
</p>{% endif %}
This just shows the name of the object, so I changed it to show a link to the actual path of that object thusly (the changed bit is in the "{% if inline_admin_form.original %} section):
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% if inline_admin_form.original %} {{ inline_admin_form.original.path }}{% endif %}
{% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %}
</p>{% endif %}
That definitely gave me a link to the file, but when I click it, nothing happens, and when I right-click and open it in a new tab, it just says "about:blank." What am I doing wrong?
UPDATE
Thanks in part to some research online, and ppetrid's answer below, I now know that my problem is that I'm trying to access a local file from a web server. Not being a web programmer, I didn't know that this would be a problem. Unfortunately, I'm not sure what the solution is.
I know that Django has a "File" object, and it seems like it would make sense to use that. However, disturbingly it looks as though each file added to the database that way will actually upload the file itself to a certain location. The problem is that we have literally thousands of files, exceeding 60GB in size, and basically duplicating that is not going to work. All I want is a pointer of sorts to the file, and the only "uploading" to happen when a person clicks on the link and sees the pdf in their browser. The files themselves are on the network in a DLink DNS-325 NAS device (the IP of which I can use in a url within Django). I've been accessing them through the Windows Map Network Drive, so they're also available under the "Z:/" drive on every workstation. Any way to get Django to just point to these files so I can see them in the browser?
UPDATE
I've found that Django itself has a way of serving "static" files of this type. Naturally, this is not suitable for "production," but I'll be using it to just get things working for now. I've followed the directions on the Django website about serving static files, and ended up with this in my settings.py:
STATIC_ROOT = 'C:/projects/working/'
STATIC_URL = '/static/'
The appropriate line of the tabular.html template now looks like this:
{% if inline_admin_form.original %} {{ inline_admin_form.original.relative_path }}{% endif %}
On the one hand, this seems to be working, because it's now trying to open a proper URL, but whenever it does, it gives me a 404 error, indicating the static file is not actually being served at that location. So, my NEW question is: how do I get the static files to actually be served at the correct URLs?
YET ANOTHER UPDATE
Ok, I think I've made a breakthrough. It seemed that although Django can serve static files, I wasn't doing all the steps necessary to make that happen. Now, my settings.py looks like this:
STATIC_ROOT = ''
# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = '/static/'
# Additional locations of static files
STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
'C:/projects/working/',
)
My urls.py looks like this:
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^search_results/$', views.search_results),
)
urlpatterns += staticfiles_urlpatterns()
And the template for the tabular inline looks like this:
{% if inline_admin_form.original %} {{ inline_admin_form.original.relative_path }}{% endif %}
Now, this seems to be getting much closer to what I need. I don't get a 404, a "no object with that primary key exists", or the silent failure I got when I was pointing directly to a file on my hard drive. It actually opens a new page with the correct URL, Chrome says "loading" in the lower left (like it always does when opening a pdf), but it just sits there forever. It never actually loads. Any other ideas?
Why do you use a CharField for the "path" property? Since it's a file you should use a FileField to upload the pdf and then do href="{{inline_admin_form.original.path.url}}" to get a working link.
I would also change the name from "path" to "pdf" or something. The way you do it now you try to load the file directly from your local filesystem which won't work once you deploy your project (since the file won't be uploaded to the server). You might also want to have a look here https://docs.djangoproject.com/en/dev/topics/files/#using-files-in-models
I am brand new to web development, Django, python, html, etc. I have a basic Django app that displays a list of publication titles that have been entered into the database. This works fine.
I now want to make it so that each publication title is a link that - when clicked on - renders another template with the details of the publication that was clicked. So far, I know how to get the publication link to render a template, but I am trying to figure out how to pass in the publication title to the hyperlink so that the data that is rendered in the details template will be specific to the title that was chosen.
Here is what I have in my publication template which displays all the publications (it is incorrect, but hopefully it clarifies what I am trying to do):
<html>
<head><title>Publications</title></head>
<body>
<h1>Publications</h1>
<ul>
{% for publication in publication_list %}
<li><strong>{{ publication.title}}</strong></li>
{% endfor %}
</ul>
</body>
</html>
For the sake of context, the url pattern that handles this is:
url(r'^(?P<detail_type>\w+)/(?P<link_item>\w+)/detail$', get_details)
And the view function is:
// note: I may have some of the logic/syntax wrong here, but this is the basic idea
def get_details(request, detail_type=None, link_item=None):
if detail_type == "publications":
publication = Publication.objects.filter(title__iexact=link_item)
return render(request, 'publication_detail.html', {'detail_type' : detail_type, 'publication' : publication})
elif ....
Like I said, I am very much a beginner so if I am approaching this in wrong way, any suggestions or resources are appreciated. Thanks.
If you use named url patterns you can easily do this with the url template tag.
urls.py
url(r'^(?P<detail_type>\w+)/(?P<link_item>\w+)/detail$', get_details, name='details')
template
{% url 'details' 'publications' publication.title %}
I hope you know about SlugField too, it is much better for urls than a normal CharField.
An alternative:
urls.py
url(r'^(?P<detail_type>\w+)/(?P<pk>\w+)/detail$', get_details, name='details')
template
{% url 'details' 'publications' publication.pk %}
views.py
def get_details(request, detail_type=None, pk=None):
if detail_type == "publications":
publication = Publication.objects.get(pk=pk)
return render(request, 'publication_detail.html', {'detail_type' : detail_type, 'publication' : publication})
elif ....
This uses the primary key of the entry instead of the title. If you want to have a url with the title in it you will want to add and use a SlugField on your model.
This looks pretty good to me, although you may want to use get as opposed to filter in your view function if all the publication titles are unique and you want to pass an instance of Publication rather than a queryset (containing one item) into the detail template. This would throw an error of there were 0 or >1 matches, but it sounds like that's probably the behavior you'd want
However, I'm not sure what it is that you're missing here. What does publication_detail.html look like? You should have essentially everything you need in the above code to render the details, assuming they're all contained in the relevant Publication instance.
Im trying to render a cms page, within another page using a custom cms plugin.
The this is my plugin class:
class PageInDiv(CMSPlugin):
page_div = models.ForeignKey(Page, verbose_name= "page")
def __unicode__(self):
return self.page_div.get_title()
as you can see all it does is link the plugin to a page then on my cms_plugins.py i have
class PageInDivPlugin(CMSPluginBase):
model = PageInDiv
name = _("Page in div")
render_template = "page.html"
admin_preview = False
def render(self, context, instance, placeholder):
temp = loader.get_template(instance.page_div.get_template())
html = temp.render(context)
context.update({
'html': html,
'title':instance.page_div.get_title(),
'placeholder':placeholder,
})
return context
as you can see i pass the html for the provided page to the plugin template, then the plugin template is rendered within the page thats hosting the plugin.
The problem i am having is that the placeholder content from the page thats selected via foreignkey is not being rendered ( displayed ).
So my question is, is there a way to render a pages placeholders programatically ?
Just for a moment ignoring the idea of creating a custom plugin in order to do what you describe (ie, render a page's placeholders programatically), the following might be a viable alternative, depending on what exactly you are trying to achieve...
You should be able, just in the template for your "outer" cms page (ie, the page within which you want to display the contents of another cms page), to get access to the current page like this:
{{ request.current_page }}
This is by virtue of the cms page middleware. So taking that a step further, you should be able to access the page's placeholders like this:
{% for placeholder in request.current_page.placeholders %}
{{ placeholder.render }}
{% endfor %}
That's one way you could go about rendering a page's placeholders "inside" another page.
I needed to render another page from within the template which could be accomplished with:
#register.simple_tag(takes_context=True)
def render_page(context, page, default="base.html"):
if not page:
return loader.get_template(default).render(context)
new_context = copy(context)
new_context['request'] = copy(context['request'])
new_context['request'].current_page = page
new_context['current_page'] = page
new_context['has_change_permissions'] = page.has_change_permission(context['request'])
new_context['has_view_permissions'] = page.has_view_permission(context['request'])
if not new_context['has_view_permissions']:
return loader.get_template(default).render(context)
return loader.get_template(page.get_template()).render(new_context)