django create profile or page for object - python

I have a venue object and want to have either a profile/page associated with the object which contains venue specific info like price, hours of operation and so on. Here is my venue object
class Venue(model.Models):
name=models.CharField(max_length=100)
# ...
image=models.ImageField(upload_to=...)
Now I have no idea where to go next with the VenueProfile model and the view which will bind these together this all I can think of:
class VenueProfile(model.Models):
venue=models.OneToOneField(Venue, related_name='venueProfile')
# ...
# info i want stored about the venue
# ...
Can someone please help me figure out if
This is the right type of model setup for a objects profile/page
How do I write a view for this? Connect the venue model with the profile model and render a template.

Yes, you are taking a right approach. Usually when you want to have a decoupled entity and then later if you want to associate attributes with it (e.g. profile to a user), OneToOneField is very useful.
As far as connecting, since these are two separate tables, there is no really good way of merging these. However since you are using related_name parameter, even though models are different, you can easily access attributes of the other model by:
venue = Venue.objects.get(...)
venue.name <- Venue attribute
venue.venueProfile.foo <- VenueProfile attribute
One down side of this approach is that there are database queries involved. To make this more efficient, you can do either one of these approaches. The first approach however is more efficient since for that, Django will use SQL join which is faster to "Python's" joins.
profile = VenueProfile.objects.filter(...).select_related('venue')[0]
venue = profile.venue <- no extra query
or this method for here Django will do the join in Python so slower:
venue = Venue.objects.filter(...).prefetch_related('venueProfile')[0]
At this point, these are just regular objects so you can easily pass them to template. The following is a simple view, urlconfig and template example:
def all_venues(request):
# note that querying profiles...
venues = VenueProfile.objects.all().select_related('venue')
return render_to_response('template.html', {'venues':venues})
def venue(request, venue_id):
venue = VenueProfile.objects.filter(venue__pk=venue_id).select_related('venue')
if len(venue) == 1:
venue = venue[0]
else:
raise Http404
...
urlconfig:
url(r'^venue/all/$', 'all_venues', name='all_venues'),
url(r'^venue/(?P<venue_id>\d+)/$', 'venue', name='venue'),
and template
{% load url from future %}
{% for venue_profile in venues %}
{% with venue=venue_profile.venue profile=venue_profile %}
<ul>
<li>
<a href="{% url 'venue' venue_id=venue.pk %}">
{{ venue.name }}</a><br>
<img href="{{ venue.image.url }}"><br>
{{ profile.foo }}
</li>
</ul>
{% endwith %}
{% empty %}
No venues
{% endfor %}

Related

Django generic model ListViews & forms

Following from my question Django sharing one model between two others which explored the best way to generically manage two similar models and their dependencies. I've now come to the next part - the views.
After a bit of tweaking around I've come to the following generic listview to represent the objects.
Is this coherent? Should the urls manage the models like this?
I believe if I refine their logic I can just subclass this itemlist and add additional logic in the future so it should be future-proofed.
A class that
urls.py excerpt
urlpatterns = [
url(r'^item/(?P<model>\w+)/$',views.ItemList.as_view(),name='generic'),
url(r'^item/(?P<model>\w+)/(?P<pk>\d+)$',views.ItemList.as_view(),name='generic'),
]
views.py excerpt
class ItemList(ListView):
context_object_name = 'itemlist'
template_name = 'food/item.html'
model = None
def get_queryset(self):
model = apps.get_model('food',self.kwargs.get('model'))
if self.kwargs.get('pk'):
print ('pk provided')
return model.objects.filter(pk=self.kwargs.get('pk'))
else:
print ('all')
return model.objects.all()
food/item.html excerpt (using registered filter to get class name for object)
{% block content %}
{% for item in itemlist %}
...
{% for review in item.reviews.all %}
{% endfor %}
...
<span class="glyphicon glyphicon-plus"> Add Review </span>
{% endblock %}
Also - can I carry this attitude into my forms (again assuming that when I add a review it'll generically link back to whatever object it came from?) or is that where I should start breaking them up?

Django SEO app, integrating external data like 'Best {{ product }} of the year'

I am trying to integrate external data in my metatags. What I want to achieve - by entering in Title field (in admin) "Details for {{ product.name }}" I would like to get to get automaticaly "Details for Hadron Collider" as a result in a browser. I have this in my template
{% load seo %}
{% get_metadata for product as metadata %}
{{ metadata.title }}
and I am passing Product object to the template, but what I get as a result is unchanged "Details for {{ product.name }}", so {{ value }} won't be populated / parsed? I have read the docs http://django-seo.readthedocs.org/en/latest/reference/administrators.html, it looks so simple
If you would like to reference the relevant model instance, you type the name of the model surrounded by two braces. For example Buy {{ product }} today!. You can go further and reference different fields from the relevant instance, for example By {{ product.name }} today!.
Though it doesn't work for me. Please, help. Probably I am doing something wrong.
Ok, after days of depression :) I solved it, at least for my project and currently only for Models, but for ModelInstance and Views it's almost I will make it later, it has the same problem in my case. I am quite new to Django, so a better solution may exist, I found this one.
I use Django 1.6.4, Python 2.7, multilingual project (Django Transmeta for model translation)
The problems:
1. variable substitution in models, model instances and views doesn't work. So, if we pass an object to DjangoSEO it won't substitute e.g. {{ product }}, {{ product.name }} variables. According to documentation it should. The problem, for Model is in backends.py, ModelBackend class. Function _resolve_value doesn't pass object to the function _resolve, which is supposed to populate meta tags with object properties. Here are the functions with slight changes that work for me.
def _resolve_value(self, name):
value = super(ModelMetadataBase, self)._resolve_value(name)
try:
return _resolve(value, self._content_object)
except AttributeError:
return value
def _resolve(value, model_instance=None, context=None):
""" Resolves any template references in the given value. """
if isinstance(value, basestring) and "{" in value:
if context is None:
context = Context()
if model_instance is not None:
context[model_instance.__class__.__name__.lower()] = model_instance
t = Template(value)
value = t.render(context)
return value
Also, in the file base.py, function get_linked_metadata we have to attach our object to Metadata instances like this:
if ModelMetadata is not None:
try:
model_md = ModelMetadata.objects.get(_content_type=content_type, _language=language)
except ModelMetadata.DoesNotExist:
model_md = ModelMetadata(_content_type=content_type, _language=language)
model_md._content_object = obj
instances.append(model_md)
And the problem 2 - DjangoSEO was using one Metadata instance for any language, even with option use_i18n = True. So, add _language=language as mentioned above.
Django SEO app is quite good, it has everything SEO needs (if it works :), so there is no reason to reinvent the wheel.
You need to wrap your variables in html tags.
{% load seo %}
{% get_metadata for product as metadata %}
<html>
<head>
<title>{{ metadata.title}}</title>
</head>
<body></body>
</html>

Python Django models execute a join query

I am developing an application with Django framework and I'm new to it
I need to create a query that simply joins 2 tables( type and subType ) and then later on use the result in the views the models file looks like this:
class Type(models.Model):
name = models.CharField(max_length=60)
description = models.CharField(max_length=200)
class SubType(models.Model):
name = models.CharField(max_length=60)
description = models.CharField(max_length = 200)
Type = models.ForeignKey(Type)
And in home.html file I have : (I dropped out the bootstrap code to make it more readable )
<ul>
<li ><a href="/home" >Home Page</a></li>
{% for type in all_types %}
<li >
{{ type.name }}
<ul >
--The Inner Loop--
</ul>
</li>
{% endfor %}
</ul>
I need to create an list of types and then inside each type I need to create another list which contains the subTypes of each type
I have no idea how do I have to create the query inside the views.py
def index(request):
list_all_types = #Here I have to create a join query via django models
t = loader.get_template('home.html');
c = Context({
'all_types' : list_all_types,
});
return HttpResponse(t.render(c));
So please let me know how do I have to make the query and replace the correct code with the comment part in views.py and what changes do I have to add to make to the home.html to make it enabled show all the subTypes instead of --inner loop in home.html
Thanks in advance
You don't need to do anything in the view other than get the types:
types = Type.objects.all()
In the template, you just iterate over type.subtype_set.all:
<ul>
{% for subtype in type.subtype_set.all %}
<li>{{ subtype.name }}</li>
{% endfor %}
</ul>
Note that this is well covered by the tutorial. (As is the use of the render shortcut rather than loading and rendering the template separately.)
(Actually, what I said at first was not quite true: for the sake of efficiency, you can add prefetch_related() to the Type query.)

Possible to limit filters ManyToMany/Foreign Key in Django admin for a model where the relationship is defined on the other model?

So the title is a bit obtuse, I know, but I couldn't think of a more succinct way to state it. Here's the issue:
I've created two proxy models for "user types", both inheriting from django.contrib.auth.User. Each has a custom manager limiting the queryset to items belonging to a particular Group. Specifically, there's a PressUser which is any user belonging to the "Press" group and StaffUser which is any user in any other group than "Press".
The issue is that when I add 'groups' to list_filters on my StaffUsers modeladmin, the resulting filter options are every group available, including "Press", and not just groups available to StaffUsers.
I've research a bit online and came up with a custom filterspec that should produce the behavior I want, but the problem is that the User model's 'groups' attribute is actually a related_name applied from the Group model. As a result, I can't attach my filterspec to 'groups' in my proxy model.
Is there any other way to apply the filterspec? Alternatively, is there a better approach to filtering the items returned by the default filterspec?
So, I was able to solve my own problem. For those that might run into a similar situation, here are the steps:
The approach I took is to modify the change_list.html template and manually filter out the items I didn't want to be included. There's quite a number of changes to make, though.
First, add a changelist_view method to your ModelAdmin:
# myproject/account/admin.py
class StaffUserAdmin(models.ModelAdmin):
...
def changelist_view(self, request, extra_context=None):
groups = Group.objects.exclude(name__in=['Press',]).values_list('name')
extra_context = {
'groups': [x[0] for x in groups],
}
return super(StaffUserAdmin, self).changelist_view(request,
extra_context=extra_context)
Basically, all we're doing here is passing in the filtered list of Groups we want to use into the context for the template.
Second, create a change_list.html template for your app.
# myproject/templates/admin/auth/staffuser/change_list.html
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% load i18n %}
{% load account_admin %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% ifequal spec.title 'group' %}
{% admin_list_group_filter cl spec groups %}
{% else %}
{% admin_list_filter cl spec %}
{% endifequal %}
{% endfor %}
</div>
{% endif %}
{% endblock filters %}
This one deserves a little explanation. First, the template tag loads: admin_list is used for the default Django template tag responsible for rendering the filters, admin_list_filter, i18n is used for trans, and account_admin is for my custom template tag (discussed in a sec), admin_list_group_filter.
The variable spec.title holds the title of the field that's being filtered on. Since I'm trying to alter how the Groups filter is displayed, I'm checking if it equals 'groups'. If it does, then I use my custom template tag, otherwise, it falls back to the default Django template tag.
Third, we create the template tag. I basically just copied the default Django template tag and made the necessary modifications.
# myproject/account/templatetags/account_admin.py
from django.template import Library
register = Library()
def admin_list_group_filter(cl, spec, groups):
return {'title': spec.title, 'choices' : list(spec.choices(cl)), 'groups': groups }
admin_list_group_filter = register.inclusion_tag('admin/auth/group_filter.html')(admin_list_group_filter)
The only things that I've changed here are adding a new argument to the method called 'groups' so I can pass in my filtered list of groups from before, as well as adding a new key to the dictionary to pass that list into the context for the template tag. I've also changed the template the tag uses to a new one that we're about to create now.
Fourth, create the template for the template tag.
# myproject/templates/admin/auth/group_filter.html
{% load i18n %}
<h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</h3>
<ul>
{% for choice in choices %}
{% if choice.display in groups %}
<li{% if choice.selected %} class="selected"{% endif %}>
{{ choice.display }}</li>
{% endif %}
{% endfor %}
</ul>
No big surprises here. All we're doing is putting all the pieces together. Each choice is a dictionary with all the values needed to construct the filter link. Specifically, choice.display holds the actual name of the instance that will be filtered by. Obviously enough, I've set up a check to see if this value is in my filtered list of groups I want to show, and only render the link if it is.
So, it's a bit involved but works remarkably well. Just like that, you have a list of filters that is exactly what you want instead of the default ones generated by Django.
I'm going to tell you off the bat that I've never done this before myself, so take it with a grain of salt.
What I'd suggest would be to override get_changelist on your ModelAdmin, to return a custom ChangeList class, which you can define somewhere in your admin module.
Your custom ChangeList class would simply override get_filters, so you can map your custom FilterSpec for the group field.
Another thing that might interest you are patches from the feature request ticket for specifying custom filter specs. The latest patch doesn't work for Django 1.3rc1 yet, although #bendavis78 recently posted that he's working on a new one, but depending on your version of Django it may apply cleanly.
It looks like it barely missed the cut to get included into the 1.3 milestone, so I figure it's going to make it into the trunk as soon as work beings on Django 1.4.

A help in concept and a database query question (Django/Python)

I am trying to build a kind of news website for learning purposes.
class NewsCategory(models.Model):
category = models.CharField(max_length=50)
Note: A category can be Soccer, Tennis, business ... User can register to different news category. This choice will be saved in their preferences.
class Profile(models.Model):
user = models.ForeignKey(User, unique=True)
gender = models.CharField(max_length=1,blank=True)
preference = models.ManyToManyField(NewsCategory)
I am currently stuck on how to update the preference list of each user (the list that specifies in which categories he is interested in.)
View:
category = [(item.category) for item in NewsCategory.objects.all()]
and then I am sending the category to the template below
template:
<div id="c_b">
{% for c in category %}
<input type="checkbox" name="category[]" value="{{c}}">
<label for="{{c}}">{{c}}</label>
{% endfor %}
</div>
Questions:
What is the best way to add the checked tag next to the checkboxes that are already saved in the user's preference when I display the template.
I though of getting all the preferences users are registered for: saved_preference = user.preference.all() and then checking for each item in category if it is in saved_preference
I am also blanking out on the way to actually write that into code, and whether this should be done in the view or the template.
What is a good way to update the user preference list?
I was planning on running user.preference.clear() and then going through every item in the submitted form and running user.preference.add(the_new_preference)
You'll need to pass the complete list of categories and also an index of user-selected categories to your template. You don't need to convert the NewsCategory queryset into a list in your view, by the way:
View
categories = NewsCategory.objects.all()
user_preferences = [item.id for item in Profile.preference.all()]
The user_preferences variable will act as a lookup index for our template.
Then you loop through all the categories in the template, and check to see if it exists in the list of user preferences:
Template
<div id="c_b">
{% for c in categories %}
<input type="checkbox" name="category[]" id="id_{{ c.category }}" value="{{ c.id }}" {% if c.id in user_preferences %}checked="checked"{% endif %} />
<label for="id_{{ c.id }}">{{ c.category }}</label>
{% endfor %}
</div>
Update - saving user preferences
There is no hard and fast rule here. The main consideration, as far as I am concerned, would be minimising database hits. You can just clear the user preferences, like you say, and add the new ones - in fact, this is how Django's admin handles it. Just make use of Django's transaction management:
from django.db import transaction
#transaction.commit_manually
def add_preferences(user, preferences):
user.preference.clear()
for pref in preferences:
user.preference.add(pref)
transaction.commit()
You should learn about forms and model forms in django. It would be the best way for both adding and changing. Forms will do the most of job for you.

Categories