database design for user/reviewer in django database model - database

i am kinda new to database design, but i would like to create three tables "User" and "Review" and "Topic" for a database in django.
I will try to explain it in detail here:
For example, I have User, Topic and Review models in models.py. one user can only write one review for one topic from other users.
let's say: Mike, John, Peter are the three Users.
Mike posted "Hello World" topic. John can only write one review for the topic "Hello World", Peter can also write one review for the same. John and Peter can not post another review for the same topic(they can only modify it). If Mike post another topic, John and Peter can post another review for the new topic. the same rule apply to other users.
please if you could, could you please provide some sample code for this issue? thanks a lot.

If you are trying to figure out how to set up your models.py, visit the django documentation, and look at Writing your first app (https://docs.djangoproject.com/en/dev/intro/tutorial01/). It goes from start to finish writing your first application and you will learn how the system works.
If you wanted more specifics for the paradigm of your case, here's what I would do. I would probably handle this in the view/template and submit/edit the review with Dajaxice calls to the database. If a review by the current user exists, it will show the data, if it doesn't it will be a blank entry that will use Dajax to submit the content. In the python method that the Dajax calls, you would try to find a review, and if one exists while attempting to add a new one, something went wrong and you can handle the error, otherwise it is saved for all to see.
For example, in models.py:
class User(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Review(models.Model):
title = models.CharField(max_length=64)
message = models.TextField()
topic = models.ForeignKey(Topic)
user = models.ForeignKey(User)
def __unicode__(self):
return self.title
class Topic
title = models.CharField(max_length=64)
message = models.TextField()
user = models.ForeignKey()
def __unicode__(self):
return self.title
in views.py:
class Post(models.Model): # This is defined a model, but not part of the data layer, it really is view code.
topic = None
your_review = None
other_reviews = None
def __unicode__(self):
return ''
def GetDetails(request):
posts = () # to be returned to and looped by the Template.
topics = Topic.objects.all().order_by('-posting_date') # posting_date descending.
for t in topics:
post = Post()
post.topic = t
post.your_review = Review.objects.filter(topic__id=t.id, user__id=<current_user_id>)
post.other_reviews = Review.objects.filter(topic__id=t.id, ~Q(user__id=<current_user_id>)
# Append to the posts array.
posts.append(post)
return render_to_response('index.htm', {'posts': posts}, context_instance=RequestContext(request))
in your index.htm:
{% if posts %}
{% for p in posts %}
<div>
<div class="title">{{ p.topic.title }}</div>
<div class="message">{{ p.topic.message }}</div>
<div class="other_reviews">
{% if p.other_reviews %}
{% for r in p.other_reviews %}
<div class="review_title">{{ r.title }}</div>
<div class="review_message">{{ r.message }}</div>
{% endfor %}
{% endif %}
<div>
<input type="text" value="{% if p.your_review %}{{ p.your_review.title }}{% endif %}">
</div>
<div>
<textarea>{% if p.your_review %}{{ p.your_review.message }}{% endif %}</textarea>
</div>
</div>
</div>
{% endfor %}
{% endif %}

Related

Get Image and intro text from wagtail page chooser

I am incorporating a page chooser in wagtail admin, however what I want to get is the image for each item in the streamblock for the page chooser and was wanting to know if there is a way to do this. So lets say if I have a category index page I would have the following code:
{% if page.case_study %}
{% image page.case_study.image fill-50x50-c100 class="" %}
{{ page.case_study }}
{% endif %}
All I seem to get are the links which is fine, but I need the image from that case study page. My model is as follows:
class CategoryPage(Page):
"""
Detail view for a specific category
"""
introduction = RichTextField(
help_text='Text to describe the page',
blank=True)
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
)
body = StreamField(
BaseStreamBlock(), verbose_name="Page body", blank=True
)
case_study = StreamField([
('Cases', blocks.PageChooserBlock()),
], blank=True,
null=True,
verbose_name='Case Studies',
)
origin = models.ForeignKey(
Country,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
category_type = models.ForeignKey(
'categories.CategoryType',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
categories = ParentalManyToManyField('Category', blank=True)
content_panels = Page.content_panels + [
FieldPanel('introduction', classname="full"),
ImageChooserPanel('image'),
StreamFieldPanel('body'),
StreamFieldPanel('case_study'),
FieldPanel('origin'),
FieldPanel('category_type'),
]
search_fields = Page.search_fields + [
index.SearchField('body'),
]
parent_page_types = ['CategoriesIndexPage']
Any help would be greatly appreciated
A StreamField is a list of blocks, but when you write page.case_study.image you're trying to access it as if it's only a single item. I'd suggest you start by updating your field / block names to make this distinction clearer - case_studies is the list, and each block in it is a single case (note that conventionally block names are lower-case):
case_studies = StreamField(
[
('case', blocks.PageChooserBlock()),
],
blank=True,
null=True,
verbose_name='Case Studies',
)
In your template, you will now loop over page.case_studies. However, this will not give you the case study page objects directly - each item in the list is a 'block' object, with block_type and value properties. This is because a StreamField usually involves mixing multiple block types (rather than just the one type you've defined here) and in that situation, you need some way of finding out what block type you're working with on each iteration of the loop.
{% if page.case_studies %}
{% for block in page.case_studies %}
{# block.value now refers to the page object #}
{% endfor %}
{% endif %}
The next issue is that your PageChooserBlock currently lets you select any page type. This means that when your template is rendered, it has no way to know up-front which page type to retrieve, so (in order to avoid unnecessary database lookups) it returns it as a basic Page instance which only contains the core fields that are common to all pages, such as title and slug - consequently, your image field will not be available through block.value.image. You can get around this by using block.value.specific.image which performs the extra database lookup to retrieve the complete page data - however, a more efficient approach is to specify the page type on the PageChooserBlock (assuming you've set up a dedicated page type for your case studies):
('case', blocks.PageChooserBlock(page_type=CaseStudyPage)),
Your final template code then becomes:
{% if page.case_studies %}
{% for block in page.case_studies %}
{% image block.value.image fill-50x50-c100 class="" %}
{% endfor %}
{% endif %}

Django Custom field's attributes making database queries

I am facing a very weird problem in one of my django projects. In my project I have a custom field class that handles foreign keys, one to one and many 2 many model fields. The class is some thing like the following.
from django import forms
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.model = model
self.limit = limit
self.multiple = multiple
self.create_objects = create_objects
super(CustomRelatedField, self).__init__(*args, **kwargs)
def clean(self, value):
""" Calls self.get_objects to get the actual model object instance(s)
from the given unicode value.
"""
# Do some value processing here
return self.get_objects(value)
def get_objects(self, values):
""" Returns the model object instances for the given unicode values.
"""
results = []
for value in values:
try:
obj = self.model.object.get_or_create(name=value)[0]
results.append(obj)
except Exception, err:
# Log the error here.
return results
def prepare_value(self, value):
""" Returns the value to be sent to the UI. The value
passed to this method is generally the object id or
a list of object id's (in case it is a many to many object).
So we need to make a database query to get the object and
then return the name attribute.
"""
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
return result
Recently while I was playing with the django-toolbar, I found out one of the pages that has a form with the above mentioned fields was ridiculously making multiple queries for the same objects again and again.
While debugging, I found out the prepare_value method was being called again and again. After some more debugging, I realized the culprit was the template. I have a generic template that I use for forms, It looks something like the following:
{% for field in form %}
{% if field.is_hidden %}
<!-- Do something here -->
{% endif %}
{% if field.field.required %}
<!-- Do something here -->
{% endif %}
<label>{{ field.label }}</label>
<div class="form-field">{{ field }}</div>
{% if field.field.widget.attrs.help_text %}
<!-- Do something here -->
{% elif field.errors %}
<!-- Do something here -->
{% endif %}
{% endfor %}
In the above code, each if statement calls the field class which calls the prepare_value method which then makes the database queries. Each of the following listed is making a database query, I am totally lost to why this is happening and have no clue about any solutions. Any help, suggestions would be really appreciated. Thanks.
field.is_hidden
field.field.required
field.label
field.label_tag
field
field.field.widget.attrs.help_text
field.errors
Also, why does this happen with my custom field class only, other fields (FKs, O2Os, M2M's) in the application and the application admin, just make one query, even though they are using a similar template.
Problem is with your prepare_value() method which does explicit queries. .get() does not get cached and always hits the db while iterating on .filter() queryset will evaluate that.
This might be causing you multiple queries.
This is not seen in default fields because they do not do any queries in prepare_value().
To resolve this, you can try to cache the value and result. If value hasn't changed, return cached result. Something like:
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.cached_result = None
self.cached_value = None
...
def prepare_value(self, value):
#check we have cached result
if value == self.cached_value:
return self.cached_result
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
#cache the result and value
self.cached_result = result
self.cached_value = value
return result
Not sure how good/bad this work around though!

Django template - compare date to today

I'm trying to make a simple IF function that checks if the date of an item is equal to today or not. Unfortunately I can't get it to work. I basically decides that the statement is false and doesn't show any content, even when it show. I am not getting any error either.
The code I use is following:
{% if item.valid_until.date == now.date %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}
The content of valid_until is a DateTimeProperty from a Google App Engine app. Normally working with this in Django template doesn't cause any problems. Solutions from similar questions on SO didn't work so far. Am I missing something obvious?
UPDATE 1:
This statement runs in a loop on the result of a database query. Therefore doing the comparison in the view didn't work as far as I could see, since I would have to send the variable with each item.
There are 2 aproach on this case:
1st:
you can add a #property on model
Model:
from datetime import date
#property
def is_past_due(self):
return timezone.now() > self.valid_until # if valid until default is timezone.now else change it
Template:
{% if item.is_past_due %}
<!--In the past-->
{% else %}
{{ item.valid_until.date|date:"Y-m-d" }}
{% endif %}
2nd:
declare a today date with format on template
{% now "Y-m-d" as todays_date %}
{% if todays_date < item.valid_until.date|date:"Y-m-d" %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}

Testing for presence of form instance in Jinja2

I'm using WTforms with Jinja2 and want to change my templates page title depending on whether I am creating a new instance of editing an existing form object.
This is what I wrote in the template:
{% block title %}{% if form.obj %}Edit{% else %}New{% endif %} Post{% endblock %}
What I expect to see:
if the form is filled out I expect to see "Edit Post" in the page title.
if the form is empty I expect to see "New Post" in the page title.
What I get: "New Post" in both instances.
Here is my PostHandler that is passing the form values.
def with_post(fun):
def decorate(self, post_id=None):
post = None
if post_id:
post = models.BlogPost.get_by_id(int(post_id))
if not post:
self.error(404)
return
fun(self, post)
return decorate
class PostHandler(BaseHandler):
def render_form(self, form):
self.render_to_response("edit.html", {'form': form})
#with_post
def get(self, post):
self.render_form(MyForm(obj=post))
#with_post
def post(self, post):
form = MyForm(formdata=self.request.POST, obj=post)
if post and form.validate():
form.populate_obj(post)
post.put()
post.publish()
self.render_to_response("published.html", {'post': post})
elif self.request.POST and form.validate():
post = models.BlogPost()
post.title = form.title.data
post.body = form.body.data
post.tags = form.tags.data
post.publish()
self.render_to_response("published.html", {'post': post})
else:
self.render_to_response('edit.html', {'form':form})
In short, all I'm trying to do is test whether the form is filled, and change my page title "New Post" or "Edit Post" accordingly.
While Form accepts a obj argument, it doesn't actually store it, it just uses that obj to fill in any blanks that formdata didn't provide. So when you ask Jinja2 {% if form.obj %} it's always going to be False, because there is never a obj property (unless you have a field that happens to be called obj of course).
If you're editing a post, you'll have an id to work with so you know which post to update in the database, so where are you currently storing that? Assuming you store it as a hidden field, you could just do:
{% if form.id.data == None %}Must be a New form {% endif %}
If you wanted to check if the entire form was empty, you could access the form.data dictionary, and make sure all the entries are None, although you need to be careful, because I know that FileField returns a u'None' instead of a real None, so you'd have to double check what Fields you care about.

Guestbook application

I made a very minor mod to the GqlQuery to retrieve only specified records using the
'where' keyword. The output, however, displays all entries from the guestbook db!
(I need to filter the data by author)
Guestbook5_datastore code:
#greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
greetings = db.GqlQuery("SELECT * FROM Greeting where greeting.author='mike'")
index.html code:
{% for greeting in greetings %}
{% if greeting.author %}
<b>{{ greeting.author.nickname }}</b> wrote:
{% else %}
An Anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.content|escape }}</blockquote>
{% endfor %}
Your author property is not a string, so I don't think you can do
greeting.author='mike'
I'm surprised that you wouldn't get an error telling you that though, rather than it returning them all!
You're attempting to filter based on a property of another entity, which would require a join. This isn't supported in App Engine.

Resources