Django: Obtaining the absolute URL without access to a request object - python

I have a model like the one below. When an instance is created, I want to send out an e-mail to an interested party:
class TrainStop(models.Model):
name = models.CharField(max_length=32)
notify_email = models.EmailField(null=True, blank=True)
def new_stop_created(sender, instance, created, *args, **kwargs):
# Only for new stops
if not created or instance.id is None: return
# Send the status link
if instance.notify_email:
send_mail(
subject='Stop submitted: %s' % instance.name,
message='Check status: %s' % reverse('stop_status', kwargs={'status_id':str(instance.id),}),
from_email='admin#example.com',
recipient_list=[instance.notify_email,]
)
signals.post_save.connect(new_stop_created, sender=TrainStop)
However, the reverse call only returns the path portion of the URL. Example: /stops/9/status/. I need a complete URL like http://example.com/stops/9/status/. How would I go about retrieving the hostname and port (for test instances that do not use port 80) of the current website?
My initial thought was to make this available via a variable in settings.py that I could then access as needed. However, thought someone might have a more robust suggestion.

There's the sites framework, as yedpodtrzitko mentioned, but, as you mentioned, it's very much a manual setup.
There's requiring a setting in settings.py, but it's only slightly less manual than setting up sites. (It can handle multiple domains, just as well as sites and the SITE_ID setting can).
There's an idea for replacing get_absolute_url, that would make stuff like this easier, though I think its implementation suffers from the same problem (how to get the domain, scheme [http vs https], etc).
I've been toying with the idea of a middleware that examines incoming requests and constructs a "most likely domain" setting of some sort based on the frequency of the HTTP HOST header's value. Or perhaps it could set this setting on each request individually, so you could always have the current domain to work with. I haven't gotten to the point of seriously looking into it, but it's a thought.

For getting current site there's object Site:
If you don’t have access to the request object, you can use the
get_current() method of the Site model’s manager. You should then
ensure that your settings file does contain the SITE_ID setting. This
example is equivalent to the previous one:
from django.contrib.sites.models import Site
def my_function_without_request():
current_site = Site.objects.get_current()
if current_site.domain == 'foo.com':
# Do something
pass
else:
# Do something else.
pass
More info: http://docs.djangoproject.com/en/dev/ref/contrib/sites/

Related

How to set assignee to ticket in Zenpy python client library

I am integrating Zendesk ITSM API in my python library using Zenpy client.
For creating ticket using zenpy client , I can set reporter of ticket by email id, but for assignee I am not able to set assignee by email id.
This is the code I have tried so far.
ticket_payload = Ticket(
description="some description",
type="task",
priority="high",
status="open"
)
ticket_payload.requester = User(email="requester1#gmail.com"])
ticket_payload.assignee = User(email="assignee1#gmail.com")
response = self.zenpy_client.tickets.create(ticket_payload)
Response ticket I get has reporter set, but assigneee is None.
If I pass id along with the email as
ticket_payload.assignee = User(id= 354876354,email="assignee1#gmail.com")
then it works and I can see assignee is set to ticket. But this is not required for reporter.
Is this the defult behaviour of zenpy, or am I missing something?
It took me a while but I think I understand your issue. From the doc in the API Objects section under the Object Properties section:
When the assignee attribute is accessed, Zenpy first attempts to
locate the related User in the User cache and if it cannot be found
will generate and execute an API call to retrieve, instantiate, cache
and return the object.
I mistakenly assumed that is meant I could set the value of the assignee attribute and zenpy would look it up and then put the id in the assignee_id field. This is an incorrect interpretation. The quote means if you set the assignee_id and then attempt to access the assignee attribute it will look it up and return it. But there must be an id.
From the next section of the docs:
It is important to note that most property setters throw away all
information except for the id. This is because Zendesk only expects
the id, so any modifications made to the object will not be persisted
automatically.
So when you set the assignee or requestor without an id, the email address is thrown away and the respective id is set to None.
As to why the requester ID is set, it is probably set to the User that you use to authenticate the request rather than the one that you specify.

Django session data obtainable from models.py without key

I want to access a session variable from a model, in order to customize what gets shown in a template using {{item.seller_email}}. relevant code from models.py:
SESSION_STORE = import_module(settings.SESSION_ENGINE).SessionStore
class Item(Action):
def seller_email(self):
seller = self.get_seller()
session = SESSION_STORE()
debug('session: %s' % vars(session))
viewer = fetch(Client, email = session.get('client', None))
administrator = viewer.is_admin() if viewer else False
debug('viewer: %s, administrator: %s' % (viewer, administrator))
if administrator:
return seller.email
else:
return seller.anonymized[16:]
the answer here seems to say that this is not possible nor desired, but I lack the understanding of Django's session mechanism to be sure: Access session / request information outside of views in Django
this seems to indicate that the current session can be retrieved outside of views, though, just by not passing a key: https://docs.djangoproject.com/en/1.6/topics/http/sessions/#using-sessions-out-of-views, but I've tried it both that way and the above way with the same results: an "empty" session is created.
I'm sure there are other, better ways of doing this, and I will find one myself, but can someone familiar with Django internals explain why this is not possible?
Because the model shouldn't know anything about session, that creates unnecessary entanglement(?) beetween different components and violation of MVC pattern.
What I suggest to do, is to call the function, specifying parameters needed to get a proper result, viewer object in this case.
If using the code in the question, how do you expect your model to work if it's used in a management command (where there is no http request and as a result no session) ?
Or from a celery job ?

Why does full=true breaks resources behaviour in Django Tastypie?

Using full=trueon related fields is very convenient feature to prevent client from doing too many requests to get necessary information.
Nevertheless, I really don't understand why the "instructions" in Meta Class are not followed as soon as you use a related field.
Here is a simple example:
class UserResource(ModelResource):
class meta():
queryset = User.objects.all()
resource_name = 'users'
authorization = NothingAuthorization() # Returns None or [] on GET requests
class ClientUserResource(ModelResource):
user = fields.ForeignKey(UserResource, 'user', full=True)
class meta():
queryset = ClientUser.objects.all()
resource_name = 'client_users'
# Some other required fields
Then,
a GET request on /users/1 returns a 401 response (just as expected, perfect!)
a GET request on /client_users/1 (client_user 1 is related to user 1) returns the data of client_user 1 AND the data of user 1 (I didn't expect to get user 1 information since it's unauthorised.)
Note that I got the same behaviour with allowed_methods, validation, etc.
This behaviour is a burden to me since it forces me to remove full=true everywhere to avoid security holes.
What am I doing wrong?
What do you expect Tastypie to do in that case? I suppose you would like to see a 401 Unauthorized to your ClientUserResource GET, but in my opinion this would be rather inconsistent. You are specifying that ClientUserResource has no explicit Authorization defined (and thus free GET access) but following your logic you would like to see the response according to another resource's authorization.
If you want to avoid security holes, it would be best indeed to not use full=True. You could overwrite the hydrate method to return the expanded object or not according to the rules you want, or use a similar Authentication class for ClientUserResource that takes into consideration the rules of UserResource (in this case full=True would be ok as you are controlling the full resource access).
EDIT: My two proposed solutions with more detail.
1. Same authorization
With this I mean that, if you want to protect your related resource while using full=True, you should use the same authorization in both resources (or a more strict authorization in ClientUserResource so your UserResource never gets leaked). In fact, I can't think of a case in which you would like to have two complete distinct authorization logics and then include one resource inside of another.
In your example, you should add authorization = NothingAuthorization(). If you really want a different authentication behavior for the second class let's see another option.
2. Overwrite dehydrate method or obj_create
In this case, you should delete the full=True and provide that behavior yourself, by for instance overwriting the dehydrate method. In this method you have access to your request and data so you can perform some logic.
class ClientUserResource(ModelResource):
class meta():
queryset = ClientUser.objects.all()
resource_name = 'client_users'
def dehydrate(self, bundle):
# Do whatever you want, for instance, authorize only one user, and then add
# to the result your desired info as full=True would do
if bundle.request.user.username == "admin":
bundle.data['user'] = {'username': "admin",
'email': "admin#gmail.com",
...}}
return bundle
This can look kind of "ugly", as you are not using any Authorization class in your resource that provides the authorization logic. I suppose you could build in the dehydrate method a UserResource along with the request and test there the authorization, but I think it gets more and more complicated for the strange behavior we are expecting here (different authentication models for two resources which are very coupled).

KindError in Google App Engine

I defined a simple class in GAE for keeping user profiles data like this:
class User(db.Model):
email = db.EmailProperty()
role = db.StringProperty(default=roles.USER)
first_name = db.StringProperty()
last_name = db.StringProperty()
...
I use memcache to keep session information. memcache data looks like this { 'key': 'agpjYW5kaXJhdGVzcgoLEgRVc2VyGCMM'}. I get session_id value from the cookie. When I try to get user info linked to that cookie like this:
session_id = request['session_id']
data = memcache.get(session_id)
user = User.get(data['key'])
I get KindError exception:
KindError: Kind 'User' is not a subclass of kind 'User'
I know this user exists, memcache exists. User class is defined only once in my project. Why this error occurs and how can I make it work?
UPDATE: I tried to use db.get() instead of User.get() and it worked. So, what's the problem there can be?
Model.get() does check whether the supplied key is of the correct kind, as defined in the documentation. If not of the correct kind it will throw a KindError.
db.get() does not do any type checking and therefore will succeed with the supplied value if it exists in the data store, but will not necessarily return a User entity.
So you need to check whether the key in your memcache is actually of the User kind. Are you sure it's not overwritten with the key of a different model at some point?
The App Engine framework defines a class called 'User' as part of the Users API. In addition, you have your own class by the same name. When the exception occurs, you're trying to use one, but getting the other.
To avoid this, rename your model. You should also be careful how you import modules in Python. Instead of:
from google.appengine.api.users import User
or worse:
from google.appengine.api.users import *
you should use:
from google.appengine.api import users
And then refer to users.User, which is unambiguous.
The problem, it seems to me, is more subtle than that. I was getting the error with this call to Model.get() (I'm retrieving a top-level singleton object, always there):
datastore = GDSDatastore.get(gds.Key.from_path(*path))
so I investigated with this code:
datastore = gds.get(gds.Key.from_path(*path))
if not(datastore is None or isinstance(datastore, GDSDatastore)):
logger.error("KindError isinstance(GDSDatastore)=%s class=%s" % (isinstance(datastore, GDSDatastore), datastore.__class__.__name__))
raise gds.KindError('Kind %r is not a GDSDatastore instance' %
(datastore.kind()))
The vast majority of the time I get no error, but today I got this interesting log:
KindError isinstance(GDSDatastore)=False class=GDSDatastore
Now, that strikes me as rather peculiar.
(Note: GDSDatastore is defined locally: class GDSDatastore(gds.Model))

Django ease of building a RESTful interface

I'm looking for an excuse to learn Django for a new project that has come up. Typically I like to build RESTful server-side interfaces where a URL maps to resources that spits out data in some platform independent context, such as XML or JSON. This is
rather straightforward to do without the use of frameworks, but some of them such as Ruby on Rails conveniently allow you to easily spit back XML to a client based on the type of URL you pass it, based on your existing model code.
My question is, does something like Django have support for this? I've googled and found some 'RESTful' 3rd party code that can go on top of Django. Not sure if I'm too keen on that.
If not Django, any other Python framework that's already built with this in mind so I do not have to reinvent the wheel as I already have in languages like PHP?
This is probably pretty easy to do.
URL mappings are easy to construct, for example:
urlpatterns = patterns('books.views',
(r'^books/$', 'index'),
(r'^books/(\d+)/$', 'get'))
Django supports model serialization, so it's easy to turn models into XML:
from django.core import serializers
from models import Book
data = serializers.serialize("xml", Book.objects.all())
Combine the two with decorators and you can build fast, quick handlers:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
def xml_view(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return HttpResponse(serializers.serialize("xml", result),
mimetype="text/xml")
return wrapper
#xml_view
def index(request):
return Books.objects.all()
#xml_view
def get(request, id):
return get_object_or_404(Book, pk=id)
(I had to edit out the most obvious links.)
+1 for piston - (link above). I had used apibuilder (Washington Times open source) in the past, but Piston works easier for me. The most difficult thing for me is in figuring out my URL structures for the API, and to help with the regular expressions. I've also used surlex which makes that chore much easier.
Example, using this model for Group (from a timetable system we're working on):
class Group(models.Model):
"""
Tree-like structure that holds groups that may have other groups as leaves.
For example ``st01gp01`` is part of ``stage1``.
This allows subgroups to work. The name is ``parents``, i.e.::
>>> stage1group01 = Group.objects.get(unique_name = 'St 1 Gp01')
>>> stage1group01
>>> <Group: St 1 Gp01>
# get the parents...
>>> stage1group01.parents.all()
>>> [<Group: Stage 1>]
``symmetrical`` on ``subgroup`` is needed to allow the 'parents' attribute to be 'visible'.
"""
subgroup = models.ManyToManyField("Group", related_name = "parents", symmetrical= False, blank=True)
unique_name = models.CharField(max_length=255)
name = models.CharField(max_length=255)
academic_year = models.CharField(max_length=255)
dept_id = models.CharField(max_length=255)
class Meta:
db_table = u'timetable_group'
def __unicode__(self):
return "%s" % self.name
And this urls.py fragment (note that surlex allows regular expression macros to be set up easily):
from surlex.dj import surl
from surlex import register_macro
from piston.resource import Resource
from api.handlers import GroupHandler
group_handler = Resource(GroupHandler)
# add another macro to our 'surl' function
# this picks up our module definitions
register_macro('t', r'[\w\W ,-]+')
urlpatterns = patterns('',
# group handler
# all groups
url(r'^groups/$', group_handler),
surl(r'^group/<id:#>/$', group_handler),
surl(r'^group/<name:t>/$', group_handler),)
Then this handler will look after JSON output (by default) and can also do XML and YAML.
class GroupHandler(BaseHandler):
"""
Entry point for Group model
"""
allowed_methods = ('GET', )
model = Group
fields = ('id', 'unique_name', 'name', 'dept_id', 'academic_year', 'subgroup')
def read(self, request, id=None, name=None):
base = Group.objects
if id:
print self.__class__, 'ID'
try:
return base.get(id=id)
except ObjectDoesNotExist:
return rc.NOT_FOUND
except MultipleObjectsReturned: # Should never happen, since we're using a primary key.
return rc.BAD_REQUEST
else:
if name:
print self.__class__, 'Name'
return base.filter(unique_name = name).all()
else:
print self.__class__, 'NO ID'
return base.all()
As you can see, most of the handler code is in figuring out what parameters are being passed in urlpatterns.
Some example URLs are api/groups/, api/group/3301/ and api/group/st1gp01/ - all of which will output JSON.
Take a look at Piston, it's a mini-framework for Django for creating RESTful APIs.
A recent blog post by Eric Holscher provides some more insight on the PROs of using Piston: Large Problems in Django, Mostly Solved: APIs
It can respond with any kind of data. JSON/XML/PDF/pictures/CSV...
Django itself comes with a set of serializers.
Edit
I just had a look at at Piston — looks promising. Best feature:
Stays out of your way.
:)
Regarding your comment about not liking 3rd party code - that's too bad because the pluggable apps are one of django's greatest features. Like others answered, piston will do most of the work for you.
A little over a year ago, I wrote a REST web service in Django for a large Seattle company that does streaming media on the Internet.
Django was excellent for the purpose. As "a paid nerd" observed, the Django URL config is wonderful: you can set up your URLs just the way you want them, and have it serve up the appropriate objects.
The one thing I didn't like: the Django ORM has absolutely no support for binary BLOBs. If you want to serve up photos or something, you will need to keep them in a file system, and not in a database. Because we were using multiple servers, I had to choose between writing my own BLOB support or finding some replication framework that would keep all the servers up to date with the latest binary data. (I chose to write my own BLOB support. It wasn't very hard, so I was actually annoyed that the Django guys didn't do that work. There should be one, and preferably only one, obvious way to do something.)
I really like the Django ORM. It makes the database part really easy; you don't need to know any SQL. (I don't like SQL and I do like Python, so it's a double win.) The "admin interface", which you get for free, gives you a great way to look through your data, and to poke data in during testing and development.
I recommend Django without reservation.

Categories

Resources