I have set up a few models as follows:
Page
Gallery
Image
Gallery and Image share a many to many relationship.
Page can have 1 or 0 galleries.
I have created a gallery with a few images and attached it to a page object.
However, I'm having trouble displaying it in a template.
Can anyone help?
My page view looks like this:
def detail(request, page_id):
p = get_object_or_404(Page, pk=page_id)
return render_to_response('page/detail.html', {'page': p},
context_instance=RequestContext(request))
And the template:
{% block images %}
{% if page.gallery %}
{% for image in page.gallery.image_set.all %}
<a rel="gallery" href="{{ STATIC_URL }}{{ image.image }}"></a>
{% endfor %}
{% endif %}
{% endblock %}`
If I try to output some text within the IF block it comes out OK but not within the loop.
Any advice on getting it to work / generally tidying this up would be greatly appreciated.
Thanks.
If the Gallery-Image relationship is many to many I'm guessing you have an m2m field on your Gallery model, where the m2m manager is the field itself. The manager can be queried just like any other QuerySet: model_instance.mym2mfield.filter().
If your ManyToManyField is called images on the Gallery model, your template would look like this:
{% for image in page.gallery.images.all %}
<a rel="gallery" href="{{ STATIC_URL }}{{ image.image }}"></a>
{% endfor %}
If your ManyToManyField is on your Image model, the default name would be as you describe, unless you specified a related_name in which case you'd use that name.
{% for image in page.gallery.image_set.all %}
Your problem may lie in the way you're accessing the gallery from the page. In your template, try something like the following and see if you get any output:
{{ page.gallery }}
There are instances where certain errors may get ignored in a template.
Also, I'm a little curious how your Page and Gallery are related? Does the Page model have a OneToOneField called "gallery"? Does Gallery have a ForeignKey to Page? In the latter case, you'd access the galleries associated with a page by:
{{ page.gallery_set.all }}
It would be easier to answer this question if you provided some additional information about the fields on your models.
Related
In my Django app (research database), when changing a person object in the admin, I'd like all of the sources for that person to be listed as hyperlinks to the file for that source. I'm trying to do this by creating a custom template for a stacked inline. Here is the custom template so far:
<p>Testing</p>
{% for form in inline_admin_formset %}
{% for fieldset in form %}
<h5>Fieldset</h5>
{% if fieldset.name %} <h2>{{ fieldset.name }}</h2>{% endif %}
{% for line in fieldset %}
<h6>Line</h6>
{% for field in line %}
<h6>Field</h6>
{{ field.field }}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
A lot of this is just for me to see what's going on. I used the links here and here as sort of a guide. What renders from the {{ field.field }} is what you'd expect from an inline element - a dropdown menu with the source names as choices and some icons for adding/changing.
What I really want, however, is just the source name rendered as a hyperlink. How do I get the source name (the actual name of the attribute is source_name) from what I have in the Django template language (i.e. the "field" object)?
In that context, {{ field.field }} is a BoundField object, and the value method is probably what you would want to use, as is in {{ field.field.value }}.
A more Django-ish approach (and more complicated) might involve creating a custom widget (start by subclassing one of their built-ins) that only displays text, and then hook that into the form being used in the ModelAdmin for your model. I think there's a bit of a rabbit hole there, in terms of needing to subclass the BaseInlineFormset and possibly a few others down that chain... I'm seeing that the BaseFormSet class has a .form attribute referenced in its construct_form method, but things are little murky from there.
Might also be useful to check out this past thread: Override a form in Django admin
I am pretty new to Flask/Flask-Admin.
I have followed the tutorial on flask admin and managed to get the admin panel working but slightly lost on how to get the below things implemented.
https://github.com/flask-admin/flask-admin/tree/master/examples/auth
When logged in as a normal user I can only see "home" page.
How can I expose other views to "normal user" and restrict actions such as read only etc.
I have created a "baseview" which is not associated with any other models as below:
class SitesView(MyBaseView):
#expose('/')
def index(self):
return self.render('views/testviews.html')
admin.add_view(SitesView(name='Test views', endpoint='test views'))
and html as below:
{% extends 'admin/master.html' %}
{% block body %}
{{ super() }}
{% if current_user.has_role('view1') %}
Site1
{% endif %}
{% if current_user.has_role('view2') %}
<a>Site2</a>
{% endif %}
{% if current_user.has_role('view3') %}
<a>Site3</a>
{% endif %}
{% if current_user.has_role('view4') %}
<a>Site4</a>
{% endif %}
{% endblock %}
This gives me a new tab with different views with works as expected.
What I am trying to achieve here is when user click the Site1 link they go to Site1 page within flask-admin interface but I am not sure how to do that. I could create a new route for this but the problem is I can't(don't know how to) extend flask admin template.
For example this works but it redirect the page outside flask-admin template:
#app.route('/views/')
def views():
return render_template('views/views1.html')
and modified the templates>admin>index.html page with below:
<ul class="lead text-center list-group">
{% if current_user.has_role('view1') %}
<li class="list-group-item">View1</li>
{% endif %}
{% if current_user.has_role('view2') %}
<li class="list-group-item">View2</li>
{% endif %}
{% if current_user.has_role('view3') %}
<li class="list-group-item">View3</li>
{% endif %}
{% if current_user.has_role('view4') %}
<li class="list-group-item">View4</li>
{% endif %}
</ul
I want to build the whole web site using flask admin so that I can keep user experience consistence. Am I doing this the wrong way?
Thanks for your time.
Please do let me know if you want me to provide more information on this issue.
Kind Regards.
So after going through documentations and tutorials I have found the solution to my issue.
For my first question:
When logged in as a normal user I can only see "home" page. How can I
expose other views to "normal user" and restrict actions such as read
only etc.
We can do this by overwriting our view functions is_accessible method as below:
def is_accessible(self):
if not current_user.is_active or not current_user.is_authenticated:
return False
if current_user.has_role('superuser') or current_user.has_role('user') or current_user.has_role('view1'):
return True
return False
For my second question we just need to give the endpoint as for our BaseView as below:
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('views.html')
admin.add_view(MyView(name='Custom Views', endpoint='customviews'))
And then in your jinja template you need to call it:
href="{{ url_for('customviews.index') }}
Just one thing to note, doing this:
current_user.has_role('superuser') or current_user.has_role('user') or current_user.has_role('view1')
could get quite messy if we have so many roles, not sure how we would approach this but hoping this will help someone.
Thanks all.
I know this is an old question, but for the following code
current_user.has_role('superuser') or current_user.has_role('user') or current_user.has_role('view1')
What I like to do is having a hybrid_property (available on both Peewee and SQLAlchemy) inside my User class that consolidates these properties. So it'd look something like this:
#hybrid_property
def user_has_administrative_rights(self):
return self.has_role('superuser') or self.has_role('user')
I have several models in my application, and as I will have some views doing the same thing (form + tables showing existing records in my model), but implemented dfferently because of the model, which will be different in each case, I was wondering if it was possible to make it generic.
I googled a bit and was not able to find anything relevant to my case.
What I would like to achieve:
In my view, I want to go through each object from the model that I passed to the template, for example:
return render_template('addstatus.html', form=form, statuses=Status.query.all(),
target_model="Status", fields=Status.__mapper__.c.keys())
But I want to have only one view, whatever the model will be, so I am not able to know in advance the fields of my model, the number of columns and so on.
I want to have something like that in my view:
{% for obj in objects %}
{% for field in obj.fields %} (not existing)
<h1> {{ field }} :: {{ obj.fields.field.value }}
{% endfor %}
{% endfor %}
Is it something possible? How can I achieve that?
You can add this method to your db.Model class (either by subclassing or by monkeypatching):
from sqlalchemy.orm import class_mapper, ColumnProperty
def columns(self):
"""Return the actual columns of a SQLAlchemy-mapped object"""
return [prop.key for prop in class_mapper(self.__class__).iterate_properties
if isinstance(prop, ColumnProperty)]
Then you can use {% for col in obj.columns() %} to iterate over the column names.
I had a similar issue, and wanted something purely Jinja2 based. This will return all of the keys/fields in your "obj" table, and worked well for me:
obj.__table__.columns._data.keys()
Here it is implemented in your sample code
{% for obj in objects %}
{% for field in obj.__table__.columns._data.keys() %}
<h1> {{ field }} :: {{ obj[field] }} </h1>
{% endfor %}
{% endfor %}
Is it possible to change the display of inlines in order to change it to this? I was thinking of changing the admin template file "tabular.html", is it going to be possible or should I change something else?
Update
Ok, I've been trying to edit the tabular.html but my experience with Django isn't enough to understand how/where to make the necessary changes... Any clue of where I should start?
Shouldn't I be changing the CSS also?
I guess that {{ field.field }} automatically renders the dropdown menu (Django admin default) if I'm understand this correctly...
Update 2
I was able to change the second column to the functionality that I wanted but I think that for the first one it's going to be trickier... Current status
Update 3
One hack that I think would work is to display on each of the inlines only one of the options of the first field and then deactivate the "add another option". How can I iterate on the options in "tabular.html" ?
Update 4
I guess the trick should be done here... How can I iterate on the field choices in order to display only one choice per line?
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{{ field.get_choices_display }}
<td class="{{ field.field.name }}">
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{{ field.field.errors.as_ul }}
{{ field.field }}
{% endif %}
</td>
{% endfor %}
{% endfor %}
{% endfor %}
Yes, you could change template of your InlineModelAdmin instance to your customized template, for example customized_inline.html. Simply copy django/django/contrib/admin/templates/admin/edit_inline/tabular.html to customized_inline.html in your template path as starting.
edit
Perhaps I was misunderstanding. If you want to change the rendering style of a form field, the normal way is to change its widget. In Django ModelAdmin and InlineModelAdmin, the main ways of customizing a field widget goes around BaseModelAdmin.formfield_for_dbfield method inside django/contrib/admin/options.py, reading the code and the doc when you want to change the widget of a form field.
For field having choices, you could simply set radio_fields in ModelAdmin/InlineModelAdmin instance to render the field as radio select instead of dropdown.
Furthermore, use OneToOneField instead of ForeignKey, or set extra and max_num in your InlineModelAdmin instance to prevent admin from rendering multiple rows of inline, like:
class SomeInlineAdmin(admin.TabularInline):
model = Foo
extra = 1
max_num = 1
I cannot open your second link, you could post things in the question instead of using a external link.
This relates to adding information in a Foreignkey field in the main admin screen.
I have a Staff model. I need mugshots in 3 formats. I'd like to use django-imagekit for processing.
I've made a Photo model to store the mugshot, and added it as a Foreignkey to Staff as photo = models.ForeignKey(Photo).
Question: How can I add the mugshot directly in the Staff admin screen, i.e. avoid having to upload the mugshots separately in their own admin screen then link to them from Staff?
Thank you for your thoughts ...
Depends on what screen you need it in, but I guess you need it in the details/edit screen?
The most simple method is creating a template in your app, called:
/templates/admin//staff/change_form.html
In this template place the following:
{% extends 'admin/change_form.html' %}
{% block content %}
{% if not add and original.photo %}
<img src="{{original.photo.url}}" alt="Photo" />
{% endif %}
{{ block.super }}
{% endblock %}
This places the image above the rest of the content if in edit modus (so no image in add mode).
If you need it in the list you can add a custom function to your admin.py as well returning the HTML snippet for showing the photo.