Merge fields in user created email templates in django - python

So I'm going to have users type up a draft document for email confirmation for a booking application. This draft could look something like this:
Dear [contactname],
This is your booking confirmation for your vacation from [startdate] to [enddate] for a total of [numdays] days.
Please click [cancellink] to cancel your booking.
An email only makes sense in relation to a booking object. Currently I have a method that takes a booking object and a merge string and decides what to do like:
def merge_field(booking, ms):
if ms == 'contactname':
return booking.contactsheet.name
elif ms == 'startdate':
return str(booking.firstday)
elif ms == 'enddate':
return str(booking.lastday)
elif ms == 'numdays':
return str((booking.firstday - booking.lastday).days + 1)
elif ms == 'cancellink':
return 'cancel'
This hardcoded approach is pretty ugly. I would much rather prefer to have the users supply a string I could parse and infer some meaning like references into the booking object, some arithmetic and static strings (like in the link). I imagine a model like:
class MergeField(models.Model):
name = models.CharField(max_length=100)
formula = models.TextField()
Then the users could design and test their own merge fields and use them in the email templates. This would make it much easier to maintain. However, I have no idea how I would parse this formula field.
Do you have any idea how to do this?

A better solution would be to use the django template system. Have the user enter the message in django template format like:
Dear {{contactname}},
This is your booking confirmation for your vacation from {{startdate}} to {{enddate}} for a total of {{numdays}} days.
Please click {{cancellink}} to cancel your booking.
And then use get_template_from_string from django.template.loader and get a template instance. Than you can render the template by doing template.render(context), Where context is a Context instance found in django.template.context.
You can read more about django template api in https://docs.djangoproject.com/en/1.6/ref/templates/api/ Should be simple from here.

Related

How to populate choice form from db in Django?

I can't figure out how to populate choice form from db. I know about ModelChoiceForm but the problem seems to be slightly different.
I want user to choose which sector does he work in. For example: 'Finance','Electronics' etc. which I would do simple:
SECTOR_CHOICES = (('finance',_('Finance'),
'electronics',_('Electronics')...
))
But the problem is that I want admin of the web to be able to add new choices, remove choice etc.
What came to my mind is to create a simple Model called Sector:
class Sector(models.Model):
name = models.CharField(max_length=40)
and User would have new attribute sector = models.ModelChoice(Sector).
But I'm scared what would happend when admin changes or removes a sector which is already used, and more, what if he removes it and the sector attribute is required?
How to solve this problem?
I would just override the delete_model as custom action and there check if the selected sector object is in use.
def delete_model(modeladmin, request, queryset):
for obj in queryset:
if UserModel.objects.filter(sector=obj).exists():
# do not delete, just add some message warning the admin about it
else:
obj.delete()
class UserModelAdmin(admin.ModelAdmin):
actions = [delete_model]
# ...

Creating two separate registration forms in Web2py?

So I want to create two registration forms side by side. One for users and other for employees. So basically the index page will have something like "Click here if you are a user" and "Click here if you are an employee". It will redirect to the appropriate registration page. I want the user registration to be just like how the built in web2py registration is. For the employee registration I want the following fields:
Name 2. Store Name 3. Store Type 4. Zip Code
I am really new to web2py so not sure how to implement this. Could someone please tell me how I should go about creating a registration.py model for this? Also I want the index to redirect to these two links as appropriate:
[app]/user/register
[app]/employee/register
Also what would my controller file look like? Would I need a separate controller for user and the employee?
Your question is not quite clear. Do you want to show two forms side by side OR do you want to redirect to the appropriate registration page?
Let's assume you opt for the second option as you described in your question. I'm also assuming that for whatever reason, employees and users are not the same as i understood from your question.
First create the employee table in models:
STORE_TYPE = ['Department store', 'Discount store', 'Warehouse store', 'Mom-And-Pop', 'Boutique']
db.define_table('employee',
Field('first_name'),
Field('last_name'),
Field('store_name'),
Field('store_type', requires=IS_IN_SET(STORE_TYPE)),
Field('zip_code'),
auth.signature)
Then in the controller just ask the user if she is an employee or user:
def index():
form = SQLFORM.factory(Field('user_or_employee', requires = IS_IN_SET(['user', 'employee']))).process()
if form.accepted:
if form.vars.user_or_employee == 'user':
redirect(URL('user/register'))
elif form.vars.user_or_employee == 'employee':
redirect(URL('employee_register'))
return locals()
If the user is a 'user' then you'll redirect them to the user/register as wished. if they are an 'employee' then redirect them to index/employee_register
def employee_register():
form = SQLFORM(db.employee)
if form.process().accepted:
redirect(URL('welcome')) # or whatever function you wish...
return locals()
From there you can take it by yourself.
Don't forget to create the views. For index and for default/employee-register.html. In both views you should include the forms you've created, something like that:
{{extend 'layout.html'}}
<h2>Please Register to Continue</h2>
{{=form}}

Properly Defining Relationships in Django DB

I'm working on a fitness game web app. The idea is that each week has a certain number of tasks that a person must complete before the next week is revealed. Here is my models.py schema for the app so far:
from django.db import models
from django.contrib.auth.models import User
class WeekOne(models.Model):
squats = models.PositiveIntegerField()
lunges = models.PositiveIntegerField()
skipStairs = models.BooleanField()
stairDaysCount = models.PositiveSmallIntegerField()
# Set to true if (squats == 1000), (lunges == 250),
# (skipStairs is True), and (stairDaysCount == 3)
weekOneComplete = models.BooleanField()
class UserProfile(models.Model):
user = models.OneToOneField(User)
weekOne = models.ForeignKey(WeekOne)
I'm lost on a few points. First of all, obviously I want each user to be able to track their own progress and not have any other users be able to see it. Is making weekOne a ForeignKey the best way to do this? If so, how does accessing each user's data work once this relationship is defined? For example, if I wanted to create an addSquats function, would I do something like this:
user = UserProfile()
user.squats += 5
or would I have to do some other magic to get to the squats field?
Second, every time a user makes a change to their progress (i.e., adds a squat or lunge), I want to check if all of the fields have met a certain a benchmark. If they have, I want to set weekOneComplete to true. Each addition will be triggered by some javascript when a user clicks a button -- but where would I put the function that checks/updates the database?
First of all, obviously I want each user to be able to track their own progress and not have any other users be able to see it.
In the template, you can do like this
User: {{ user.username }}
squats: {{ user.userprofile.squats }}
lunges: {{ user.userprofile.lunges }}
Skip Stairs:
{% if user.userprofile.skipStairs %}
Yes
{% else %}
No
{% endif %}
Stair: {{ user.userprofile.stairDaysCount }}}
Week Completed:
{% if user.userprofile.weekOneComplete %}
Yes
{% else %}
In-Progress
{% endif %}
How does accessing each user's data work once this relationship is defined? For example, if I wanted to create an addSquats function.
To access user data,
user = UserProfile(user=request.user)
user.weekOne.squats += 5
user.weekOne.save()
user.save()
Second, every time a user makes a change to their progress (i.e., adds a squat or lunge), I want to check if all of the fields have met a certain a benchmark. If they have, I want to set weekOneComplete to true.
Under your UserProfile model, create check updates function.
class UserProfile(models.Model):
user = models.OneToOneField(User)
weekOne = models.ForeignKey(WeekOne)
def check_updates(self):
check = WeekOne.object.get(id=self.weekOne)
if check.skipStairs and \
check.squats == 1000 and \
check.lunges == 250 and \
check.stairDaysCount == 3:
check.weekOneComplete = True
check.save()
So every time you update the data for that user, just call the method like this:
user = UserProfile.objects.get(user=request.user)
user.check_updates()
To access field of week one you need to do:
user.weekOne.squats += 5
user.weekOne.save()
Actually, it is better to use the F function and do (or similar):
user.weekOne.squats = F('squates') + 5
In general, it is better to do all checkings also in the server side and not to rely on the client (JS or whatever). E.g You expose a url and check for all the POST params that they are integers (e.g. converted to ints)
To your second question: one possible (and simple) way of doing this, since you say you're using javascript, is through an ajax call.
It should point to a url which, itself, points to a view that'll handle the data you're sending in the request. Something along the lines of (say you're using a POST request):
def add(request):
if not request.is_ajax():
raise ...
excercise, amount = request.POST['excercise'], request.POST['amount']
user = request.user
# a model method that'll add to whatever activity
# the user did and update the "week one complete" field
user.did_activity(excercise, amount)
if user.weekOne.weekOneComplete:
return HttpResponse(json.dumps(some_return_data), mimetype="application/json")
return HttpResponse(json.dumps(other_return_data), mimetype="application/json")
This is more to the side of pseudocode, so you get the idea. You'd still need to write the ajax call on the JS side, the model method that'll add to the correct excercise and validate the benchmark, and save the user to the database. For example:
def did_activity(self, excercise, amount):
if excercise == 'squats':
self.weekOne.squats += amount
...
if self.hit_benchmark():
self.weekOne.weekOneComplete = True
self.save()
Also, this example assumes the user is authenticated.
Good luck.

django: keep each users data separate

I am trying to workout how / the best, most secure way to keep a user's data separate within a django site that I need to write.
Here is an example of what I need to do...
example app ToDoList
Using django contrib.auth to manage users / passwords etc, I will have the following users
tom
jim
lee
There will be a ToDo model (in my real app there will be additional models)
class ToDo(models.Model):
user = models.ForeignKey(User)
description = models.CharField(max_length=20)
details = models.CharField(max_length=50)
created = models.DateTimeField('created on')
The issue that I am having - and may be over thinking this: How would this be locked down so tom can only see Tom's todo list, lee can only see his todo list and so on...
I have seen a few posts stating that you could use filter in every query, or use urls, so the url could look like www.domain.com/username/todo
But either way I am not sure if this is the right way / best way, or bonkers in terms of stopping users seeing each others data
cheers
Richard
One approach is to filter the ToDo items by the currently logged in user:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from your_app.models import ToDo
#login_required
def todos_for_user(request):
todos = ToDo.objects.filter(user=request.user)
return render(request, 'todos/index.html', {'todos' : todos})
This locks down the view for authenticated users only, and filtering by the logged in user from the request, another user, even if logged in, can't access another user's ToDo records. Hope that helps you out.
Make url like www.domain.com/username/todo is one way to implement it, but it doesn't guarantee you achieve security.
What you should do keep your user's login information in a session data after user login, and every time you check certain view,
check whether that particular user has right to see this view.
using user's login info (ID, or username) when querying user's Todo list.
And I guess this link will help you to do your job.
Sessions, Users, and Registration.

Inline-like solution for Django Admin where Admin contains ForeignKey to other model

I have several Customers who book Appointments. Each Appointment has exactly one customer, though a customer can be booked for multiple appointments occurring at different times.
class Customer(model.Model):
def __unicode__(self):
return u'%s' % (self.name,)
name = models.CharField(max_length=30)
# and about ten other fields I'd like to see from the admin view.
class Appointment(models.Model):
datetime = models.DateTimeField()
customer = models.ForeignKey("Customer")
class Meta:
ordering = ('datetime',)
Now when an admin goes to browse through the schedule by looking at the Appointments (ordered by time) in the admin, sometimes they want to see information about the customer who has a certain appointment. Right now, they'd have to remember the customer's name, navigate from the Appointment to the Customer admin page, find the remembered Customer, and only then could browse their information.
Ideally something like an admin inline would be great. However, I can only seem to make a CustomerInline on the Appointment admin page if Customer had a ForeignKey("Appointment"). (Django specifically gives me an error saying Customer has no ForeignKey to Appointment). Does anyone know of a similar functionality, but when Appointment has a ForeignKey('Customer')?
Note: I simplified the models; the actual Customer field currently has about ~10 fields besides the name (some free text), so it would be impractical to put all the information in the __unicode__.
There is no easy way to do this with django. The inlines are designed to follow relationships backwards.
Potentially the best substitute would be to provide a link to the user object. In the list view this is pretty trivial:
Add a method to your appointment model like:
def customer_admin_link(self):
return 'Customer' % reverse('admin:app_label_customer_change %s') % self.id
customer_admin_link.allow_tags = True
customer_admin_link.short_description = 'Customer'
Then in your ModelAdmin add:
list_display = (..., 'customer_admin_link', ...)
Another solution to get exactly what you're looking for at the cost of being a bit more complex would be to define a custom admin template. If you do that you can basically do anything. Here is a guide I've used before to explain:
http://www.unessa.net/en/hoyci/2006/12/custom-admin-templates/
Basically copy the change form from the django source and add code to display the customer information.
Completing #John's answer from above - define what you would like to see on the your changelist:
return '%s' % (
reverse('admin:applabel_customer_change', (self.customer.id,)),
self.customer.name # add more stuff here
)
And to add this to the change form, see: Add custom html between two model fields in Django admin's change_form
In the ModelAdmin class for your Appointments, you should declare the following method:
class MySuperModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if obj:
# create your own model admin instance here, because you will have the Customer's
# id so you know which instance to fetch
# something like the following
inline_instance = MyModelAdminInline(self.model, self.admin_site)
self.inline_instances = [inline_instance]
return super(MySuperModelAdmin, self).get_form(request, obj, **kwargs)
For more information, browser the source for that function to give you an idea of what you will have access to.
https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L423
There is a library you can use it.
https://github.com/daniyalzade/django_reverse_admin
But if you want to use link to object in showing table you can like this code:
def customer_link(self, obj):
if obj.customer:
reverse_link = 'admin:%s_%s_change' % (
obj.customer._meta.app_label, obj.customer._meta.model_name)
link = reverse(reverse_link, args=[obj.customer.id])
return format_html('More detail' % link)
return format_html('<span >-</span>')
customer_link.allow_tags = True
customer_link.short_description = 'Customer Info'
And in list_display:
list_display = (...,customer_link,...)

Categories

Resources