I have decided to use Django-Simple-History for building a history of my models. In turn using that to build the dashboard. I have run into a bit of a snag though. I'm trying to output [User] [added,changed, deleted] [object] on/at [time] but I can't figure it out for the life of me.
So far I am able to display the historical record on the template but I can't access anything else, am I missing something?
I was hoping someone with knowledge of Simple History can help, since I couldn't get a hold of the author.
Here is the code snippets I have so far.
Models.py
from simple_history.models import HistoricalRecords
class Project(django.db.models.Model):
...
history = HistoricalRecords()
Views.py
#login_required
def addTMProject(request):
user = request.user
if request.method == 'POST':
form = TimeMaterialsForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_by = request.user
today = datetime.date.today()
project.pre_quote = "%s-" % (str(today.year)[2:4])
project.quote = Project.objects.latest().quote+1
project.save()
project.history.all()
...
And I have also passed it on my dashboard/views.py so I have access to it.
#login_required
def view_dash(request):
today = datetime.date.today()
user = request.user
proj_perm = user.has_perm('project.add_project')
project = Project.objects.all().order_by('-proj_name')
query = Project.objects.all().order_by('-id')[:5]
que_quotes = Project.objects.filter(status__value__exact = 'Quote')
expired = FollowUp.objects.filter(next_followup__lte=today).order_by('next_followup').filter(archived=False)
log = LogEntry.objects.select_related().all().order_by("-id")
hist = Project.history.all()
return render_to_response('dashboard/home.html', {'user': user, 'project': project, 'query':query, 'que_quotes':que_quotes, 'expired':expired,
'proj_perm':proj_perm, 'log': log, 'hist':hist,}, context_instance=RequestContext(request))
And finally a snippet from my template. As it is right now, the {{ h }} shows "Testing Simple Records as of 2011-04-29 10:43:57" on the template
home.html
{% if user.is_authenticated %}
<div id="large_box">
<h5>Activity</h5>
{% for h in hist %}
<ul>
<li>{{ h }}</li>
</ul>
{% endfor %}
If anyone could help or point me to some more in depth documentation, then that would be great!
Thanks everyone!
Django-Simple-History simply creates a model (and associated database table) that mirrors the object you tie it to and adds four additional fields: history_id, history_date, history_type, and history_object.
history_id: standard primary key
history_date: datetime for when the change occurred
history_type: one of +, ~, -. (+ means added, ~ means changed, and - means deleted)
history_object: representation of the model that history is being stored for
So, at most basic level, you can roughly get "[added,changed, deleted] [object] on/at [time]" in your output, using something to the effect of:
{{ h.history_type }} {{ h.history_object }} on/at {{ h.history_date }}
You'll probably want to create template tag or something to convert the +, ~, and - to the more understandable, 'Created', 'Changed', 'Deleted'. {{ h.history_object }} should return the object's __unicode__, I'm assuming, so you might need to make some modifications there or return something like {{ h.history_object.__class__ }} or {{ h.history_object._meta.verbose_name }}, instead. (Not sure if those will actually work in practice though.) And, of course, you can apply the date filter to {{ h.history_date }} to make it any format you want.
Getting the user is more difficult. Django-Simple-History doesn't seem to store this data, so there's no record of what user made the modification. However, since it basically duplicates the object as it existed, you could probably get away with adding a modified_by field to your model and filling that with request.user pre-save. Then, when Django-Simple-History does its thing, that field would be copied over like the rest and be available via {{ h.modified_by }}.
I'm assuming that the only problem you're having is with the displaying of the historical data and not the actual saving portion of it.
I'm not sure what fields you have in your Project model, but it looks like the history field is treated like a foreign key field. This foreign key's table contains the same fields that your Project model does. So, if you want to access the fields you'd have to do something like this in your template:
...
{% for h in hist %}
<ul>
<li>{{h.field1}} {{h.field2}} {{h.field3}} on {{h.field4}}</li>
</ul>
{% endfor %}
...
I found this page (http://qr7.com/2010/10/django-simple-history-ftw/) which was quite helpful, but you'll have to play around with the history field names. I wasn't quite sure what they actually were.
Hope that helps.
Related
I am working on a Django project and I want to retrieve the category name in my template like Adventure, Hiking.. but instead, it's displaying ids of the category like 1,2,3. Instead of displaying the name of the category. Can someone help me out with this?
{% for data in tourData %}
{{data.category}}
{% endfor %}
models.py
class Tour(models.Model):
category_choices=[('1','Adventure'),('2','Trekking'),('3','Hiking')]
category=models.CharField(max_length=1,choices=category_choices,default='1')
view.py
def recommendations(request):
if request.method=='POST':
contents=Tour.objects.all()
category1= request.POST['category'] #Retrieves the category entered by the user
category2=request.POST['place']
tourData = Tour.objects.all().filter(category=category1,place=category2).order_by('-rating').values()
context={
'tourData':tourData
}
return render(request,"base/recommendations.html",context)
else:
tourData=Tour.objects.all().order_by('-rating').values()
context={'tourData':tourData
}
return render(request,"base/recommendations.html",context)
You need to use get_field_display in your template, i.e.
{{ data.get_category_display }}
This will show the second tuple value in your model choices rather than the first (the first is what actually goes into the database).
As an aside, I would recommend changing the format of your tourData variable from camelCase to snake_case - tour_data - it's more pythonic.
There's a model represents BBS.
I want to make change the value without update database. because I want to preserve the database value. To be precise at display time, I want it to be displayed as '[collabo]' + article.title this is what I am doing at the moment.
below is combine '[collabo]' and all of title with for loop
for article in articles:
article.title = '[collabo]'+article.title
is there any way to change the title value at one line of code? I don't want to change or update database. Or is there a better way.
If you want to do this in a single database query it's one line longer than what you have now!! but it's far more efficient.
from django.db.models import Value
from django.db.models.functions import Concat
Article.objects.annotate(new_title = Concat(V('[collabo]'),'title')))
The annotate method in the queryset is your friend here (with a little help from Concat and Value)
You could also do this at the template level
articles = Article.objects.all()
render('template.html',{'articles': articles})
And then
{% for article in articles %}
[collabo] {{ article.title }}
{% endfor %}
you may work with methods of the model class to give specific modification you want.
models.py
class Article(models.Model):
# some fields ...
def edited_title(self):
return '[collabo] {}'.format(self.title)
then you can exploit it in the templates with {{article.edited_title}}.
I need a certain context variable in my base.html. This is to contain a set of usernames, e.g. [name1, name2, name3, name4,]. If a logged in user's username is part of this list, I give said user certain preferential treatment and show something in the navbar.
To achieve this, I wrote a template tag:
from django import template
from django.db import models
from django.contrib.auth.models import User
register = template.Library()
VIPS = [name1, name2, name3, name4,]
#register.simple_tag
def verified(user):
return VIPS
register.simple_tag(verified)
And then in base.html, I added {% load verified %} at the top, and then:
{% if user.username in verified %}
<!-- do something -->
{% endif %}
This isn't working. What am I doing wrong? I suspect I've written my template tag incorrectly, but I've tried several, more complex approaches (in vain), at least this simpler one made logical sense to me.
My project's a legacy Django 1.5 project with Python 2.7.
You don't need the register.simple_tag(verified) line, as the #register decorator is already doing that.
However, you might consider a different approach to avoid additional processing in the template, assuming your user is coming from request.user...
#regsiter.assignment_tag(takes_context=True)
def check_user_is_verified(context):
user = context['request'].user
return user and user in vips
Then in your template:
{% check_user_is_verified as is_verified %}
{% if is_verified %}
{# whatever #}
{% endif %}
By leveraging an assignment tag, you can check if the user is verified once, and leverage the context variable you assign instead of having to perform the same list processing each time.
Another alternative is to use a cached property on a custom User object, or a "Profile" model that is linked to your User model via a OneToOneField.
from django.utils.functional import cached_property
class Profile(models.Model):
user = models.OneToOneField(User)
#cached_property
def is_verified(self):
# get the list of vips here
return self.user in vips
If your list of vips changes, just clear the cache key, which you could do via a signal or a Celery task, etc:
del profile_instance.is_verified
Now you have a very efficient property you can check anywhere in your code. My preference tends to be fat models, skinny views and dumb templates.
I asked this question earlier, but now I'm having trouble sorting out how to use drop downs (or even better, autofill fields) for one of the forms of a multi-form view.
The models in play are Book, BookDetails, and Genre. BookDetails is a linking table to Genre (and other similar tables) so that I can have a static list of genres etc. with unique IDs and foreign keys to BookDetails.
Right now I have this:
#views.py
def BookFormView(request):
genre = Genre.objects.all()
if request.method == "POST":
book_form = BookForm(request.POST, prefix='book')
bookdetails_form = BookDetailsForm(request.POST, prefix='bookdetails')
selected_genre = get_object_or_404(Genre, pk=request.POST.get('genre_id'))
genre.id = selected_genre
genre.save()
if book_form.is_valid() and bookdetails_form.is_valid():
book_form.save()
bookdetails_form.save()
return HttpResponseRedirect("/books/")
else:
book_form = bookForm(prefix='book')
bookdetails_form = BookDetailsForm(prefix='bookdetails)
return render(request, 'books/createbook.html',
{'book_form' : book_form,
'bookdetails_form': bookdetails_form,
'genre':genre,})
#createbook.html
<select name="genre", id="genre" form="bookform">
{% for entry in genre %}
<option value="{{ entry.id }}">
{{ entry.name }}
</option>
{% endfor %}
</select>
The form displays properly on the page, dropdown menu with options from the database included. However, when I hit submit to store the information to the database I get an error saying No Genre matches the given query The other posts on SO that regard this error don't seem to be from the same context. I think that it might be something to do with selecting a name but storing an id (for the genres), but otherwise I'm at a loss.
Normally, the way you'd do this with a form in django is not by manually pulling something out of the POST dict, but by using a ModelChoiceField:
https://docs.djangoproject.com/en/1.8/ref/forms/fields/#modelchoicefield
Was there a specific reason you didn't do that?
Also, it appears you're using the genre variable incorrectly for two different things. You initialize it with a queryset, but then try to treat it like a Genre instance later in the code. That's going to cause problems not to mention the fact that I don't think your genre.id = ... line is going to do what you expect it to.
Also, it's against style conventions to use title-casing for function names. If you're going to be doing much coding in Python, it's probably worth taking a look at the officially accepted PEP8 style guide here:
https://www.python.org/dev/peps/pep-0008/
There are a few other problems in the code but I'm not sure it's worth calling them out.
I'm trying to get my models related using ReferenceProperty, but not have a huge amount of luck. I have 3 levels: Group, Topic, then Pros, and Cons. As in a Group houses many topics, and within each topic could be many Pros and Cons.
I am able to store new Groups nice and fine, but I don't have any idea how to store topics underneath these groups. I want to link from a page with a link "New topic" underneath each group, that takes them to a simple form (1 field for now). Obviously the URL will need to have some sort of reference to the id of the group or something.
Here are my models:
class Groups(db.Model):
group_user = db.UserProperty()
group_name = db.StringProperty(multiline=True)
group_date = db.DateTimeProperty(auto_now_add=True)
class Topics(db.Model):
topic_user = db.UserProperty()
topic_name = db.StringProperty(multiline=True)
topic_date = db.DateTimeProperty(auto_now_add=True)
topic_group = db.ReferenceProperty(Groups, collection_name='topics')
class Pro(db.Model):
pro_user = db.UserProperty()
pro_content = db.StringProperty(multiline=True)
pro_date = db.IntegerProperty(default=0)
pro_topic = db.ReferenceProperty(Topics, collection_name='pros')
class Con(db.Model):
con_user = db.UserProperty()
con_content = db.StringProperty(multiline=True)
con_date = db.IntegerProperty(default=0)
con_topic = db.ReferenceProperty(Topics, collection_name='cons')
And one function for the actual page I want to show the list of Groups, and then underneath their topics:
class Summary(webapp.RequestHandler):
def get(self):
groups_query = Groups.all()
groups = groups_query.fetch(1000)
template_values = {
'groups': groups,
}
path = os.path.join(os.path.dirname(__file__), 'summary.html')
self.response.out.write(template.render(path, template_values))
And finally the html:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
<blockquote>
{{ topics.topics_name }}
</blockquote>
{% endfor %}
</body>
</html>
Something that has side effects, such as altering the store (by creating a new object for example) should NOT be an HTTP GET -- GET should essentially only do "read" operations. This isn't pedantry, it's a key bit of HTTP semantics -- browsers, caches, proxies, etc, are allowed to act on GET as read-only operations (for example by caching results and not passing a request to the server if they can satisfy it from cache).
For modifications, use HTTP verbs such as POST (most popular essentially because all browsers implement it correctly) or for specialized operations PUT (to create new objects) or DELETE (to remove objects). I assume you'll be going to use POST to support a variety of browsers.
To get a POST from a browser, you need either Javascript wizardy or a plain old form with method=post -- I'll assume the latter for simplicity.
If you're using Django 1.0 (which app engine supports now), it has its own mechanisms to make, validate and accept forms based on models. Other frameworks have their own similarly advanced layers.
If you want to avoid "rich" frameworks you'll have to implement by hand templates for your HTML forms, direct them (via some kind of URL dispatching, e.g. in app.yaml) to a handler of yours implementing with a def post(self):, get the data from the request, validate it, form the new object, put it, display some acknowledgment page.
What part or parts of the procedure are unclear to you? Your question's title focuses specifically on reference properties but I'm not sure what problem they are giving you in particular -- from the text of your question you appear to be on the right tack about them.
Edit: the OP has now clarified in a comment that his problem is how to make something like:
"<a href="/newtopic?id={{group.key.id}}" >New topic </a>"
work. There's more than one way to do that. If the newtopic URL is served by a static form, the handler for the post "action" of that form could get back to that id= via the Referer: header (a notorious but unfixable mis-spelling), but that's a bit clunky and fragile. Better is to have the newtopic URI served by a handler whose def get gets the id= from the request and inserts it in the resulting form template -- for example, in a hidden input field. Have that form's template contain (among the other fields):
<INPUT TYPE=hidden NAME=thegroupid VALUE={{ theid }}> </INPUT>
put theid in the context with which you render that template, and it will be in the request that the def post of the action receiving the form finally gets.
Just to answer the question for others as you probably figured this out:
class NewTopic(webapp.RequestHandler):
def get(self):
groupId = self.request.get('group')
# either get the actual group object from the DB and initialize topic with topic_group=object as in 'Nick Johnson's answer, or do as follows
topic = Topic()
topic.name = self.request.get("topicname")
topic.reference = groupId
topic.put()
Thankyou for the reply.
Yeah I am aware of the get vs post. The class I posted was just to actually print all the Groups().
The issue I have is I'm unsure how I use the models to keep data in a sort of hierarchical fashion, with Groups > Topics > Pros/Cons.
Grabbing data is simple enough and I am using:
class NewGroupSubmit(webapp.RequestHandler):
def post(self):
group = Groups()
if users.get_current_user():
group.group_user = users.get_current_user()
group.group_name = self.request.get('groupname')
group.put()
self.redirect('/summary')
I need another function to add a new topic, that stores it within that group. So lets say a group is "Cars" for instance; the topics might be "Ferrari", "Porsche", "BMW", and then pros/cons for each topic. I realise I'm being a little vague, but it's because I'm very new to relational databasing and not quite used to the terminology.
I'm not quite sure what problem you're having. Everything you list looks fine - the ReferenceProperties are set up according to what one would expect from your dscription. The only problem I can see is that in your template, you're referring to a variable "topics", which isn't defined anywhere, and you're not iterating through the topics for a group anywhere. You can do that like this:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
Topics:
<ul>
{% for topic in group.topics %}
<li>{{topic.topic_name}}</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
To create a new topic, just use the constructor, passing in the required arguments:
mytopic = Topic(topic_name="foo", topic_group=somegroup)
Here, somegroup should be either a Group object, or a key for a Group object.