I have search results in form of table. I want to add a detail page button which can send the id of the result to another function in view.py
so i can query it from database.
{% if sr %}
{% for k,j in sr %}
<tbody>
<tr>
<td>{{ k.id }}</td>
<td>{{ k.chromosome }}</td>
<td>{{ k.gene_id }} </td>
<td> view</td>
</tr>
</tbody>
{% endfor %}
{% endif %}
I want to send this k.id to another function
def detailed(request):
return render(request,"search/Detailed.html")
so I can again perform a query from database by this id
Since you didn't specify the version of django you're using, I'm going to assume it's 2.x. The only huge difference between that and more recent versions of 1.x is the urlpatterns. If you're using 1.11, just use the required regex's you need, as described in the docs. Either way, the principle is the same.
urls.py
urlpatterns = [
....
path('<int:some_id>/', views.detail_view, name='detail_view'),
# if django 1.11... you would use ([0-9]{4}) in place of the 'int', or for whatever max amount of numbers you'd want to capture... But I will continue for django 2.x.
]
views.py
def detail_view(request, some_id):
some_object = YourModel.objects.get(id=some_id)
return render(request, 'detail_template.html', {'some_object ': some_object})
detail_template.html
<p>{{ some_object.chromosome }}</p>
<p>{{ some_object.gene_id }}</p>
<p>View details</p>
Note that the url block above has some_object.id added to it as an argument AFTER the view it goes to in quotations. This is the easiest way.
You can also acheive the same thing with a model method by using the reverse('app_name:view_name', args=[arg_1, arg_2, etc) function, and then call the method with {{ some_object.your_method }}. But those arguments would all depend on what your url patterns and functions took.
Related
I learned the pagination basics for Django from this link:
https://simpleisbetterthancomplex.com/tutorial/2016/08/03/how-to-paginate-with-django.html
However! the only problem I have is that I want to paginate without changing url.
By that, I mean I need to paginate a table while there are multiple tables in a html page.
How do i paginate only one table???
My code:
This is the basic structure of the html template:
...
<table class="uk-table uk-table-striped">
<tbody>
{% for post_status in post.statusPage %}
<tr>
<td>{{ post_status.status_time }}</td>
<td>{{ post_status.repost_count }}</td>
<td>{{ post_status.comment_count }}</td>
<td>{{ post_status.like_count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if post.statusPage.has_other_pages %}
<ul class="uk-pagination uk-flex-center" uk-margin>
{% if post.statusPage.has_previous %}
<li><span uk-pagination-previous></span></li>
{% else %}
<li><a><span uk-pagination-previous></span></a></li>
{% endif %}
{% for i in post.statusPage.paginator.page_range %}
{% if post.statusPage.number == i %}
<li class="uk-active"><span>{{ i }}</span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if post.statusPage.has_next %}
<li><span uk-pagination-next></span></li>
{% else %}
<li><a><span uk-pagination-next></span></a></li>
{% endif %}
</ul>
{% endif %}
In side the template, there are multiple post objects
post
Each post has its own table and paginator:
post.statusPage
is the page object of the post.
Rather than building the pagination yourself and dealing with the complexity you're running into, consider using DataTables.
You can place multiple tables on a single view, and the pagination for each table is handled via javascript with no actual change in the URL. You can either insert the values into the table using standard django template tags, or you can build a simple API (using DRF or django's JSON libraries) to feed the tables via Ajax. This is a bit more advanced, but when you have a lot of data, it ensures you aren't sending it all to the user at once and hogging system memory.
I am not sure I know what you're trying to do, but it seems like your view returns a full set of tables, and you'd like the user to be able to view them one at a time, with a click in between, instead of scrolling down forever. You don't want to hit a different URL, so this needs to be handled in the client browser, and that means this is not a Django problem and hence not a Django solution. (Calling it pagination but also wanting it all to appear at the same URL and hence be the same web page is slightly confusing.)
The simplest way from where you are now might be toggling 'show-hide' html divs, that with a button created for each page. So each page is rendered but selectively presented. You'd have to modify the little script there to set all other pages to hidden.
I'm sorry I haven't got a better example for your particular situation. There are probably a bunch of more elegant javascript-heavy solutions out there, but I can't advise you on those.
If you are willing to send querystrings with a Get request for the next page, you could solve this entirely with Django, by creating links that include '?param=' type page numbers. The parameters can then be obtained from 'request.GET.getlist('param')' to return the right page. But if your view returns everything now, you might not want to get into generating and handling querystrings.
Hat I'm trying to accomplish is to delete file from server ('static' folder, to be specific).
My jinja template:
<table>
<tr>
{% for file in files_.items %}
<td data-title="title" style="text-align: center">{{ file.title }}</td>
<td data-title="download">Download</td>
{% if current_user.username == "admin" %}
<td data-title="delete" style="text-align: center">Delete</td>
{% endif %}
</tr>
{% endfor %}
</table>
and my function:
#app.route('/upload/<path:filename>/', methods=['GET', 'POST'])
#login_required
def delete(filename):
item = db.session.query(File).get(filename)
os.remove(os.path.join(app.static_folder, item.filename))
db.session.query(File).filter_by(file=filename).delete()
db.session.commit()
return render_template('dashboard.html',delete=delete)
What I'm trying to do is to after clicking on delete in html I want to delete record from database and file from the server. Right now I'm not sure if my approach to call this function is correct, since I've tried to use prints as a primitive log system and there was nothing in the terminal, co I would say function was not called. Also my guess is that I would need to pass filename to it, so Ive tried
{{ delete(filename=file.file) }}
but it returned
UndefinedError: 'delete' is undefined
{{ delete(filename=file.file) }} in template tells python "when rendering template, call function delete()". What you want to do is generate link which, when clicked, will call delete endpoint.
So, use {{ url_for('delete', filename=...) }}
I'm brand new to programming. I came up with a project to help me learn and I'm stuck already. I'm using Flask, Flask-SQLAlchemy and Flask-wtf.
I'm trying to create a club attendance system that lists members and checks them off if they are present and logs the amount they paid (either $15 for 1 lesson, or $25 for the week). I have a table that I populate from my database that looks like this:
I want to click on submit to mark the person as present but this ticks the checkbox for everyone in the list and sets the amount paid to the same for everyone.
I have tried lots of things. I have seen similar issues here and people suggesting using FieldList and FormField - I tried this with no luck. Here is my Form code:
class MemberForm(Form):
form_id = HiddenField()
member_id = DecimalField('id')
member_name = StringField('name')
attend_date = StringField('date', default=todays_date())
is_here = BooleanField('here')
has_paid = SelectField('Amount', choices=[(15, '15'), (25, '25')])
submit = SubmitField("Submit")
def __init__(self, *args, **kwargs):
super(MemberForm, self).__init__(*args, **kwargs)
read_only(self.member_name)
My controller code:
#app.route('/', methods=['GET', 'POST'])
def home():
members = Member.query.order_by(Member.name).all()
form = MemberForm()
if request.method == 'POST': # TODO form validation and database stuff
print('got this far')
print(form.data)
return render_template('index.html', title='Tong Long',
today=todays_date(), members=members,
form=form)
and the jinja2 template part:
<table width="483" border="1">
<tbody>
<tr>
<th width="271"><strong>Member</strong></th>
<th width="152"><strong>Grade</strong></th>
<th><strong>Last Seen</strong></th>
<th width="38"><strong>Paid?</strong></th>
<th><strong>Is Here?</strong></th>
<th>Submit</th>
</tr>
{% for member in members %}
<form action="" method="post" name="{{ member.id }}">
<tr>
<td>{{form.member_name(value=member.name)}}</td>
{% for g in member.grade %}
<td>{{ g.grade }}</td>
{% endfor %}
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{form.is_here}}</td>
<td>
{{ form.submit }}
</td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
Viewing the rendered HTML I can see that all the fields have the same id.
I'm starting to think this can't be done with WTForms. Will I need to use javascript perhaps (something I know nothing about). Or manually create the forms rather than using WTF? Any help appreciated!
This is very late, but perhaps it is helpful to somebody.
What calabash is doing, is create one single form and then display it multiple times in the template.
However, to achieve the desired outcome (independend forms with independend submit buttons), multiple forms need to be created within the route function. They can be passed as a list to the template and then looped over. (A simpler solution would be one form with one submit button and dynamically created "lines" for each member. See FieldList...)
Logic:
def home():
members = Member.query.order_by(Member.name).all()
forms = []
for member in members:
form = MemberForm(prefix=member.name)
form.member_name.data = member.name
forms.append(form)
# validation:
for form in forms:
if form.submit.data and form.validate_on_submit():
# do_something here for each form, e.g. write to database
return render_template('index.html', title='Tong Long',
today=todays_date(),
forms=forms,
members=members)
The different forms need to have individual prefixes. They need to be validated individually and it needs to be checked which submit-button was used.
Note: It is perhaps not a good idea to use a form field for the name, as that information is already known from the members database entry and it might not be intended to change it here. A simple text label would make more sense in that case.
The table rows in the template could look like this:
{% for form in forms %}
<form action="" method="post">
{{ form.hidden_tag() }}
<tr>
<td>{{ form.member_name }}</td>
<td>{{ members[loop.index0].grade }}</td>
<td>{{ form.attend_date }}</td>
<td>{{ form.has_paid }}</td>
<td>{{ form.is_here }}</td>
<td>{{ form.submit }}</td>
</tr>
</form>
{% endfor %}
My problem is that I need to know if a user has rated a certain model instance, BlogSite. On the page, there are multiple BlogSite instances, which have a 5-star rating system. When the current user has rated a certain instance, it should be set to read-only.
I'm running into a roadblock because if I use a model function, I need to pass 2 variables - current_user and BlogSite. I haven't been able to find how to access request.user in models.py and it's looking like I shouldn't be doing that?
The other path I went down was to create a custom filter - but I found out that I can only pass in one parameter. I would rather not do this method because I feel it would be better to keep the logic in views.py
Does anyone have ideas of how I can solve this problem?
#models.py
class BlogSite(models.Model):
#fields
#get the average rating for a blogsite
def rating_avg(self):
rating_dict = BlogSiteReview.objects.filter(blog_site=self).aggregate(Avg('review_rating'))
rating_avg = rating_dict.get('review_rating__avg')
if rating_avg:
return rating_avg
else:
#no ratings
return 0
def rated(self, current_user):
#If there is a row for the blogsitereview with this blogsite for the logged in user, return True, else False
#can I access the current user? This does not work, seems like I can't get request here.
current_user = request.user
review = BlogSiteReview.objects.filter(blog_site=self, user=current_user)
if review:
return True
else:
return False
class BlogSiteReview(models.Model):
blog_site = models.ForeignKey(BlogSite)
user = models.ForeignKey(User)
#other fields
Here is the relevant part of the view:
#views.py
def search(request, type, regionValue):
#....
#ideally, the solution would be to have a field or function in the BlogSite model
blog_sites = BlogSite.objects.filter(country=region.id, active=True)
#....
In the template I would have an if statement to add a class if rated returns True
<tr>
<td>{{ blogsite.site_name }}</td>
<td><div id="rating{{ blogsite.id }}" class="rating {% if blogsite.user_rated %}jDisabled{% endif %}" data-average="{{ blogsite.rating_avg }}" data-id="{{ blogsite.id }}"></div></td>
<td>{{ blogsite.create_date }}</td>
</tr>
I'm looking for 2 things here - does using a model method to get if a user has rated seem like the correct approach? The problem I have with this so far is that I can't find how to access the current user to use in models.py. Another idea I thought of is to pass in the request.current_user from the view somehow, but the user is not associated with BlogSite, so I can't filter on that.
I ended up taking a slightly different approach which I thought of when trying to fall asleep but couldn't get Django out of my head :) In the view I created a list of blog_sites that a certain user left a review
#views.py
rated = BlogSiteReview.objects.filter(user=request.user).values_list('blog_site', flat=True)
return render(request, page, {..., 'blogsites':blog_sites, 'rated':rated})
And in the template I added a class if that blog_site FK id was in the blog_sites for loop:
#template
{% for blogsite in blogsites %}
<tr>
<td>{{ blogsite.site_name }}</td>
<td><div id="rating{{ blogsite.id }}" class="rating {% if blogsite.id in rated %}jDisabled{% endif %}" data-average="{{ blogsite.rating_avg }}" data-id="{{ blogsite.id }}"></div></td>
<td>{{ blogsite.create_date }}</td>
<td>{{ rated }}</td>
</tr>
{% empty %}
<p>There are no registered blogsites for this country.</p>
{% endfor %}
Finally I can go to sleep now!
You could pass the result of blog_site.rated(request.user) via template parameters. If you feed your template with multiple BlogSite instances (blog_sites in your view sample) and iterate over them in the template, you could group the instance and the rated result into tuples or dicts in your view like so:
({"site": block_site, "rated": block_site.rated(request.user)} for block_site in block_sites)
And access these in the template with result.site and result.rated, respectively, assuming that result is the iteration variable (because you named your view search) in the template (replacing blogsite in your template snippet).
Hence, I personally would keep the rated method as you posted it, with the current_user parameter.
EDIT: here is an example which mixes your code (question and answer) and my proposal:
#views.py
def search(request, type, regionValue):
#....
blog_sites = BlogSite.objects.filter(country=region.id, active=True)
search_results = ({"site": block_site, "rated": block_site.rated(request.user)} for block_site in block_sites)
return render(request, page, {..., 'search_results': search_results})
And in the template:
{% for result in search_results %}
{% with blogsite=result.site rated=result.rated %}
<tr>
<td>{{ result.site.site_name }}</td>
<td><div id="rating{{ blogsite.id }}" class="rating {% if rated %}jDisabled{% endif %}" data-average="{{ blogsite.rating_avg }}" data-id="{{ blogsite.id }}"></div></td>
<td>{{ blogsite.create_date }}</td>
<td>{{ rated }}</td>
</tr>
{% endwith %}
{% empty %}
<p>There are no registered blogsites for this country.</p>
{% endfor %}
This is assuming that columns is a list containing Strings, each String is representing one of the object o's variable.
<tbody>
{% for o in objects %}
<tr>
{% for col in columns %}
<td>{{ o.col }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
Example:
class Dog(models.Model):
name = models.CharField()
age = models.IntegerField()
is_dead = models.BooleanField()
columns = ('name', 'age')
I cannot explicitly enter the object's variable name and must pass it as another list because I am trying to make a 'generic' template. Also, not all variables must be shown to the users.
I'm not familiar enough with django to know if there's something builtin for this, but... you could just define your own version of getattr as a template filter. For some reason (I'm assuming because it's a builtin function), I wasn't able to simply register the builtin as a new template filter. Either way, this is how I've defined my version:
# This is defined in myapp/templatetags/dog_extras.py
from django import template
register = template.Library()
#register.filter
def my_getattr(obj, var):
return getattr(obj, var)
To use it, you'll use it just like any other two arg template-filter:
{{ o|my_getattr:col }}
Here's a full example (don't forget the "load" directive at the top!):
{% load dog_extras %}
<table>
<tbody>
{% for o in objects %}
<tr>
{% for col in columns %}
<td>{{ o|my_getattr:col }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
If you've never made custom template-filters before, be sure to read the docs!