HTML Link to Database Item -- Clojure Web Development - database

I am currently writing a website via Clojure code that runs on a Luminus based framework.
I have a database that stores users and uploaded files (and routes which allow me to do both).
The routes call SQL functions that I have written, altering the database.
I currently am printing out the list of files like such (in HTML):
<ul class="users">
{% for item in users %}
<li>
<p>{{item.file_name}}</p>
</br> </br>
</li>
{% endfor %}
</ul>
I want to edit it to have a link to each file as well.
For example, under the <p>{{item.file_name}</p> line I could write something like:
Home
This generates me a link to "/home" for every file_name in the database.
Instead, I would like to create a link to each file_name in the database.
For example, if the first listed item was "test.txt" I would want a link to "/test.txt" and so on, through the whole list.
Is this possible to do? Thank you in advance.

You just need to change your template to create the link HTML that is specific to an item. Something like this:
<ul class="users">
{% for item in users %}
<li>
<p>{{item.file_name}}</p>
</br> </br>
</li>
{% endfor %}
</ul>
It's hard to be any more specific than that without more information. You just have to determine how to create a URL for an item. In the code above I used "/{{item.file_name}}" based on your examples, but if the URL is more complicated than that, you could add it as a separate key to the item and do something like "{{item.url}}".

Related

Filtering a Wagtail index page by fields of the child pages (user-initiated query) / (FieldError at ... cannot resolve keyword)

My Wagtail project is at heart just a very conventional listings page where users can browse items in the database and then click on any item of interest to go its detail page. But how do I allow users to filter and/or sort the listings on the main page by the contents of fields on the child pages? This most generic, ordinary task eludes me.
Let's say the database is a collection of Things. And let's say that what people find important about each Thing are (a) the year it was discovered, and (b) the country where it can be found. A user may want to browse through all the Things, but she should be able to narrow down the list to just those Things found in 2019 in Lithuania. And she should be able to sort by year or by country. Just your super-standard functionality but I can't find any guidance or figure it out myself.
Cribbing from examples of other people's work here are my models so far:
class ThingsListingPage(Page):
def things(self):
''' return all child pages, to start with '''
things = self.get_children().specific()
# do I need 'specific' above?
# Is this altogether the wrong way to fetch child
# pages if I need to filter on their fields?
return things
def years(self, things): # Don't need things parameter yet
'''Return a list of years for use in queries'''
years = ['2020', '2019', '2018',]
return years
def countries(self, things):
'''Return a list of countries for use in queries.'''
countries = ['Angola', 'Brazil', 'Cameroon','Dubai', 'Estonia',]
return countries
def get_context(self, request):
context = super(ThingsListingPage, self).get_context(request)
things = self.things()
# this default sort is all you get for now
things_to_display = things.order_by('-first_published_at')
# Filters prn
has_filter = False
for filter_name in ['year', 'country',]:
filter_value = request.GET.get(filter_name)
if filter_value:
if filter_value != "all":
kwargs = {'{0}'.format(filter_name): filter_value}
things_to_display = things_to_display.filter(
**kwargs)
has_filter = True
page = request.GET.get('page') # is this for pagination?
context['some_things'] = things_to_display
context['has_filter'] = has_filter # tested on listings page to select header string
context['page_check'] = page # pagination thing, I guess
# Don't forget the data to populate filter choices on the listings page
context['years'] = self.years(things)
context['countries'] = self.countries(things)
return context
class ThingDetailPage(Page):
year = models.CharField(
max_length=4,
blank=True,
null=True,
)
country = models.CharField(
max_length=50,
blank=True,
null=True,
)
CONTNT_PANELS = Page.content_panels + [
FieldPanel('year'),
FieldPanel('country'),
]
# etc.
The template for the listings (index) page, showing only the filter controls (sorting controls are also required, and of course the listings themselves):
{% extends "base.html" %}
{% block content %}
<section class="filter controls">
<form method="get" accept-charset="utf-8" class="filter_form">
<ul>
<li>
<label>Years</label>
<h6 class="expanded">Sub-categories</h6>
<ul class="subfilter">
<li>
<input type="radio" name="year" value="all" id="filter_year_all"
{% if request.GET.year == "all" %}checked="checked" {% endif %} /><label
for="filter_year_all">All Years</label></input>
</li>
{% for year in years %}
<li>
<input type="radio" name="year" value="{{ year }}" id="filter_year_{{ year }}"
{% if request.GET.year == year %}checked="checked" {% endif %} /><label
for="filter_year_{{ year }}">{{ year }}</label></input>
</li>
{% endfor %}
</ul>
</li>
<li>
<label>Countries</label>
<h6 class="expanded">Sub-categories</h6>
<ul class="subfilter">
<li>
<input type="radio" name="country" value="all" id="filter_country_all"
{% if request.GET.country == "all" %}checked="checked" {% endif %} /><label
for="filter_country_all">All Countries</label></input>
</li>
{% for country in countries %}
<li>
<input type="radio" name="country" value="{{ country }}" id="filter_country_{{ country|slugify }}"
{% if request.GET.country == country %}checked="checked" {% endif %} /><label
for="filter_country_{{ country|slugify }}">{{ country }}</label></input>
</li>
{% endfor %}
</ul>
</li>
</ul>
<input type="submit" value="Apply Filters"/>
</form>
</section>
{% endblock %}
The above Page models seem to work fine in Wagtail. I've created a ThingsListingPage page named "Things," and a set of child ThingDetailPage pages, each with 'year' and 'country' data. The pages display fine: The filters on the Things listings page display the (currently hard-coded) year and country items from the ThingsListingPage model. The listings page also lists the child pages on command. No complaints from the server.
But: Upon making my filter selections and clicking the submit / Apply filters button, I get an appropriate URL in the address bar (http://localhost:8000/things/?year=2019&country=Lithuania) but this error:
FieldError at /things/
Cannot resolve keyword 'year' into field.
(If I don't select a year filter but do filter on a country I get the same error on the 'country' keyword.)
SO:
How should I change the ThingsListingPage model so that I can filter on child page fields (fields of ThingDetailPage pages)? Or is there a completely different approach I should be taking, a better / everybody-knows-that's-how Wagtail way to do arbitrary, user-initiated filter and sort operations on a page's children's fields?
Just please note that in the real project there may be different page types for TinyThings, WildThings, and what not, so I'm looking for a solution that can be modified to work even when some children don't have the field(s) used in the filter(s).
I'd also appreciate any direction you might have on how sort operations should be done.
Page.get_children returns the results as basic Page instances where only the core fields such as title are available - this is because it has no way to know the expected page type of the children at the time of doing the query (see What is the difference between ChildPage.objects.child_of(self) and ParentPage.get_children()?). Adding .specific() will return the results as the more specific page types, but this won't help for filtering, since it works as a post-processing step after the main query has run (including applying any filters).
However, you can get around this by reorganising the query expression to specify the page type:
things = ThingDetailPage.objects.child_of(self)
Here, Django knows which specific page model to query on, and so all fields of ThingDetailPage are available for filtering.
This does limit you to a single page type, and there's no perfect way around that - at the database level each page type is handled by a separate table, and it's not possible to efficiently query data that's distributed over multiple tables. (Even if ThingDetailPage and TinyThingDetailPage both have a year field defined, those are distinct entities in the database, so there's not a single 'year' column that can be filtered on.) However, you may be able to restructure your models to accommodate this using multi-table inheritance. Just as Wagtail itself gives you a base Page model containing the fields common to all pages, you could define ThingDetailPage to contain the fields common to all Things (such as year) and have subtypes inheriting from that:
class TinyThingDetailPage(ThingDetailPage):
size = models.CharField(...)
Your TinyThingDetailPages will then be included in the results of ThingDetailPage.objects.child_of(self), although they'll only be returned as instances of ThingDetailPage. Again, you can add .specific() to return them in their most specific form, but this won't work for filtering - so you'll be able to filter on the fields common to ThingDetailPage (such as year) but not size.
The answer by #gasman succeeds perfectly. However, an alternative solution suggested by one of #gasman's linked posts is equally effective.
The #gasman answer above requires that child page objects be accessed through their own model (note that you need to append child_of(self) so that the query returns only those pages that belong to the current listings page:
def things(self):
''' return all child pages, to start with '''
things = ThingDetailPage.objects.child_of(self)
return things
By querying the child page model in this way you can then access the fields of the child pages without any prefix. Thus, the get_context method in my models.py (see question) works as is. Specifically, these lines work without any changes:
kwargs = {'{0}'.format(filter_name): filter_value}
things_to_display = things_to_display.filter(**kwargs)
However, it seems it's just as valid to start with the listing page and retrieve its child pages, the way the query was written in my question:
def things(self):
''' return all child pages, to start with '''
things = self.get_children().specific()
return things
But to make this work, you must then prefix your filters with the name of the child page model (with an obligatory two underscores connecting the model name with that of the field).
The filters functioned as expected and without error as soon as I added that:
kwargs = {'{0}'.format('thingdetailpage__' + filter_name): filter_value}
things_to_display = things_to_display.filter(**kwargs)
Based on these findings, I have some doubt about the explanation #gasman gives as to why my original code didn't work:
Adding .specific() will return the results as the more specific page
types, but this won't help for filtering, since it works as a
post-processing step after the main query has run (including applying
any filters).
Be that as it may, I'm adding this answer just to document an alternative solution, i.e., one that also seems to work. I have no reason to prefer the approach used in my question. Filters on child page fields now function.

AngularJs - Pass angular argument to file twig

How can I pass an Angular item to twig file included?
This is my twig file that I want to include:
<a href="javascript:void(0)" title="[{ hostess.gender == 'F' ? 'Hostess top' : 'Steward top' }]">
<img src="{{ WBASE }}/assets/img/badge/top-profile.png" class="c-badge [{ extraClass }]"/>
</a>
Instead this is my twig file where I render data through angular loop. I need to include a twig file if the condition is true:
{% if hostess.top %}
{% include '_commons/elements/ng-badge-top.twig' with {'hostess': hostess} %}
{% endif %}
But it doesn't work. How can I do it?
I think that you won't be able to do it like that.
Take in consideration that angular is front-side rendered and twig server-side.
If you want to achive that, you will probably have to craete your ng-badge-top.twig as a html and include it with angular using "ng-include".
You can also try something like this:
<div ng-if="[{ hostess.top }]"
ng-include="'{{ path('view_route', { view: 'Content/test.html.twig' }) }}'"></div>
This last piece of code will do a call to the server to get a html file and render it. Take in consideration that this controller should serve HTML files, otherwise angular won't be able to process them. Didn't tested it, but if it doesn't work tell me and we will figure out what's wrong.
If those solutions doesn't fit your needs I can give you other options :)

Django template and angularjs filtering

I have some data on the page provided by standard django queryset {{ django_data }} there is about 100 objects inside and I display it on the standard way
{% for data in django_data %}
<div class="data_block">
<h1>{{ data.title }}</h1>
<div>{{ data.description }}</div>
</div>
{% endfor %}
Now as angularJS novice I want to add this filtering/search functionality to display less objects based on user input
Search:<input ng-model="query" type="text"/>
Also I think that to allow angular to make new request from the controller is wasting of resource to get data which is already on the page http.get('django_data.json') simply doesn't make sense. So the bast way will be to send django_data as dict from django view. If I do this, how to tell angular that data are on the page inside variable called django_data? Something like this do not work.
<div class="data_block" ng-repeat="data in django_data">

Jekyll level 3 submenu

I'd like to make a three level submenu using jekyll pages.
First, i created folder that way:
Menu item 1
• menu item 1.1
• menu item 1.1.1
• menu item 1.2
• menu item 1.2.1
Menu item 2
• menu item 2.1
• menu item 2.1.1
• menu item 2.1.2
• menu item 2.1.
• menu item 2.2
and so on.
For now, my files are in folder to use this kind of link:
menuLevel1/menuLevel2/file.md
I thought i could use YAML variables to do so, but it looks like i can't render an array of all variables in my YAML. I can make a menu using:
{{ if page.menuLevel1 == "foo" and page.menulevel2 == "bar" }}
but i'm stuck with sorting item, and since i have 5 level1 option and 10 level2 options, i think it'll take a long time to make it work.
Is there any way of doing this without hassle?
I don't where to go from here.
Tahnks a lot guys.
Ju
Maybe, working with data-files is an option for you. Simply define a file menu.yml in _data-folder which contains your menu structure:
- title: "Menu item 1"
href: "/menuLevel1/file.md"
sub:
- title: "menu item 1.1"
href: "/menuLevel1/menuLevel2/file.md"
...
- title: "Menu item 3"
href: "/menuLevel1/file2.md"
in your layout or include you walk through that file with for-loops:
<!-- 1st level -->
{% for nav in site.data.menu %}
{% if nav.sub != null %}
<li>
<ul>
<!-- 2nd level -->
{% for sub in nav.sub %}
<li>
<a href="{{ site.baseurl }}{{ sub.href }}">
{{ sub.title }}
</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>
{{ nav.title }}
</li>
{% endif %}
{% endfor %}
Working with several sub-menus you would have to add another level (i.e. 3rd for loop). Alternativly you can avoid redundancy in code by using an include with a parameter ({% include param="level" %} and level=site.data.menu, nav.sub,...) which calls itself with different parameter, whenever a sub-menu is found.
I use something similar than that for generation of a navigation menu. Simply works, i can sort the menu whenever i want and i have all my configuration files together with other files in my _data-dir (you could even store everything in _config.yml - i like to work with _data).
Hope i could help...

How to render pages those are related to user roles in angular

First of all I am very new in Angular JS. I am working in a project, where I have three types of users and all three users have different kind of views. Now My question is, can it be possible after login, render only pages/views those are belong to logged in user instead of all pages? If yes then how?
I would do it on the server side. Something like this. This will enject the right angular app based on what kind of user flag is set. Of course this would mean that you had a flag on each user that indicates what type of user they are. You could just use a unique attribute of the type of user and it would work the same. This will allow unique views, and controllers for each different kind of user. I'm assuming you want one url to serve up three different types of angular app based on what the user is. If you want to control what URLs are accessible, that's something else entirely and should be handled in your django views.
{% if user.is_staff %}
<div ng-app='staffApp'>
{% elif user.is_investor %}
<div ng-app='investorApp'>
{% elif user.is_founder %}
<div ng-app="founder">
{% endif %}
<div ng-view>
</div>
</div>
Another way is just have an attirbute that each user has like
#property
def ng-app(self):
return 'investorApp'
And then in your template do
<div ng-app='{{ user.ng-app }}'>
<div ng-view>
</div>
</div>
But any way, I think this would be the correct way to solve your question :)

Resources