How to extend request.user with own functions in django? - django

I've seen some nifty code on django-ratings documentation and like to create something similar. After googling around for now 2 weeks I got no idea on how to do this.
Maybe you could help me what to search for or where to get some docs?
Code from django-ratings docs:
...
response = AddRatingView()(request, **params)
if response.status_code == 200:
if response.content == 'Vote recorded.':
request.user.add_xp(settings.XP_BONUSES['submit-rating'])
return {'message': response.content, 'score': params['score']}
return {'error': 9, 'message': response.content}
...
My Problem:
request.user.add_xp(settings.XP_BONUSES['submit-rating'])
So i'd like to do something like this:
request.user.my_shiny_function(foobar)
Thanks in advance,
Thomas

Check out proxy models: http://docs.djangoproject.com/en/dev/topics/db/models/#id8

I think the code sample you're sighting seems to have been picked from somewhere else (it's not part of the django-ratings code - a simple grep -ir "add_xp" on the source directory shows that text is only in Readme.rst).
If you could explain why you need the functionality you're looking for here, maybe we could help some more. In the mean time, you can look at rolling your own custom backend, extending the default User model and then adding other "nifty" features to it :).

Related

Object is not legal as a SQL literal value

Long time reader, first time poster, please be gentle.
I've been working on a web app using Flask and SQLAlchemy that allows users to review and comment on MMA fights. I have a list of fights in a SQL table appropriately named "fights" and I'm trying to use dynamic routing to filter through the data. I have a list of all the fights on one route like so:
#app.route('/ufc251')
#login_required
def ufc251():
return render_template('ufc251.html', fights=Fight.query.all())
which helped me make a slick page with all the fights listed, and then made another route for info on individual fights like so:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html')
so far, so good. If I click on a fight from the main page i get sent to a url fightsite/fights/<fight_id>, which is perfect. The problem that I'm having is that I can't for the life of me figure out how to call the data from the row for a single fight. If i change my route to:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
I get the error
sqlalchemy.exc.ArgumentError: Object <Fight 1> is not legal as a SQL literal value
but if i give id a value (i.e. id=1) it will display the data from the first row in my fights table, so i feel like the problem is in the (id=id) part, but after hours of scouring the internet, I can't seem to find a solution.
Any help would be greatly appreciated. And yes, I've read the other StackOverflow article on this subject, however the answer doesn't seem to apply to this situation.
Thank you in advance!
I figured it out, however I decided I'd leave the question in case anybody else has this issue.
i changed:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
to:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id.id).first())
because initially it was passing the argument 'fight_1' instead of just '1'. I hope nobody else has to spend this long trying to solve the same problem!
The use of id to hold a Fight confuses things. Then there's the double query when one would suffice.
Consider changing
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
to
fight = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=fight)

How to follow Django redirect using django-pytest?

In setting up a ArchiveIndexView in Django I am able to successfully display a list of items in a model by navigating to the page myself.
When going to write the test in pytest to verify navigating to the page "checklist_GTD/archive/" succeeds, the test fails with the message:
> assert response.status_code == 200
E assert 301 == 200
E + where 301 = <HttpResponsePermanentRedirect status_code=301, "text/html; charset=utf-8", url="/checklist_GTD/archive/">.status_code
test_archive.py:4: AssertionError
I understand there is a way to follow the request to get the final status_code. Can someone help me with how this done in pytest-django, similar to this question? The documentation on pytest-django does not have anything on redirects. Thanks.
pytest-django provides both an unauthenticated client and a logged-in admin_client as fixtures. Really simplifies this sort of thing. Assuming for the moment that you're using admin_client because you just want to test the redirect as easily as possible, without having to log in manually:
def test_something(admin_client):
response = admin_client.get(url, follow=True)
assert response.status_code == 200
If you want to log in a standard user:
def test_something(client):
# Create user here, then:
client.login(username="foo", password="bar")
response = client.get(url, follow=True)
assert response.status_code == 200
By using follow=True in either of these, the response.status_code will equal the return code of the page after the redirect, rather than the access to the original URL. Therefore, it should resolve to 200, not 301.
I think it's not documented in pytest-django because the option is inherited from the Django test client it subclasses from (making requests).
UPDATE:
I'm getting downvoted into oblivion but I still think my answer is better so let me explain.
I still think there is a problem with Shacker's answer, where you can set follow=True and get a response code of 200 but not at the URL you expect. For example, you could get redirected unexpectedly to the login page, follow and get a response code of 200.
I understand that I asked a question on how to accomplish something with pytest and the reason I'm getting downvoted is because I provided an answer using Django's built-in TestCase class. However, the correct answer for the test is/was more important to me at the time than exclusively using pytest. As noted below, my answer still works with pytest's test discovery so I think the answer is still valid. After all, pytest is built upon Django's built-in TestCase. And my answer asserts the response code of 200 came from where I expected it to come from.
The best solution would be to modify pytest to include the expected_url as a parameter. If anyone is up for doing this I think it would be a big improvement. Thanks for reading.
ORIGINAL CONTENT:
Answering my own question here. I decided to include final expected URL using the built-in Django testing framework's assertRedirects and verify that it (1) gets redirected initially with 302 response and (2) eventually succeeds with a code 200 at the expected URL.
from django.test import TestCase, Client
def test_pytest_works():
assert 1==1
class Test(TestCase):
def test_redirect(self):
client = Client()
response = client.get("/checklist_GTD/archive/")
self.assertRedirects(response, "/expected_redirect/url", 302, 200)
Hat tip to #tdsymonds for pointing me in the right direction. I appreciated Shacker's answer but I have seen in some scenarios the redirect result being 200 when the page is redirected to an undesirable URL. With the solution above I am able to enforce the redirect URL, which pytest-django does not currently support.
Please note: This answer is compliant with the auto-discover feature of pytest-django and is thus not incompatible (it will auto-discover both pytest-django and Django TestCase tests).

I need to rewrite url in django

I need to rewrite url www.example.com/product/1 to www.example.com/en/product/1 when user chooses english language. (he will click on a select box that will toggle the language and set a session called 'language')
I cannot choose the django 1.4 which supports this feature. We are advised to stick with django 1.3.
Hence I tried a middleware, but as it turns out, the middleware runs for each request resulting in a endless loop.
class urlrewrite():
def process_request(self, request):
if 'i' in request.session:
if request.session.get('i','') != 0:
print "session"
request.session['i'] = request.session['i'] + 1
else:
request.session['i'] = 0
else:
request.session['i'] = 0
print "request.session['i']", request.session['i']
if request.session.get('i','') == SOME_CONSTANT and request.session.get('django_language','') == 'en':
del request.session['i']
return HttpResponseRedirect("en/"+request.META['PATH_INFO'])
Ofcourse, it doesnt work. This runs for every single request.
Kindly help me out.
Thank you
Don't write this yourself, use someone else's hard work.
Try django-cms's solution first.
==== EDIT ====
You do not need to used django-cms, just have it installed and use their Multilingual URL Middleware. This interfaces with django's regular internationalisation machinery.
http://django-cms.readthedocs.org/en/2.3.3/advanced/i18n.html
This problem can be solved by using a little trick in your urls.py file, as shown by this part of the docs: https://docs.djangoproject.com/en/1.4/ref/generic-views/#django-views-generic-simple-redirect-to.
You keep the same view, it will simple have a different URL. I think thats what you want. Make sure you choose the 1.3 version of the docs, I believe there has been some changes.

django-paypal ipn works fine but signal is not received

I have this code at the end of my models.py file
from paypal.standard.ipn.signals import payment_was_successful
def confirm_payment(sender, **kwargs):
# it's important to check that the product exists
logging.debug('** CONFIRMED PAYMENT ***') #never reached this point
try:
bfeat = BuyingFeature.objects.get(slug=sender.item_number)
except BuyingFeature.DoesNotExist:
return
# And that someone didn't tamper with the price
if int(bfeat.price) != int(sender.mc_gross):
return
# Check to see if it's an existing customer
try:
customer = User.objects.get(email=sender.payer_email)
except User.DoesNotExist:
customer = User.objects.create(
email=sender.payer_email,
first_name=sender.first_name,
last_name=sender.last_name
)
# Add a new order
CustomerOrder.objects.create(customer=customer, feature=bfeat, quantity=1, paypal_email=sender.payer_email, invoice=sender.invoice, remarks='')
payment_was_successful.connect(confirm_payment)
The whole process runs ok. Payment is complete. return_url and cancel_url work fine. notify_url was tested from the paypal sandbox's test tools and works ok. However, signal is never received.
Signal code is placed at the end of the models.py and django-paypal code is placed inside my project's directory.
(code was 'stolen' from here)
I must be doing something completely wrong. Any help would be appreciated!
In django-paypal there are two signals for basic transactions:
payment_was_successful
payment_was_flagged
You must handle both signals.
I had this problem - and having chased around a few similar questions have found a resolution for my specific case. I mention it here in case anyone else is hitting this wall.
I've not researched it thoroughly, but it looks as though it's highly dependent on which version/repository you source your copy of django-paypal from. Specifically, the version I downloaded wasn't updated to accommodate the {% csrf_token %} idiom. To get this to work, I had to add the #csrf_exempt decorator to two views:
the ipn view in paypal.standard.views
the view loaded by the return url in my django paypal dictionary (... this one flags a highly accurate error if you have debug on).
Is django-paypal there in the settings.INSTALLED_APPS?
I don't see any other reason why the signal wouldn't be fired, otherwise.

Django's list_details views saving queryset to memory (not updating)?

I have a custom model manager that looks like this:
class MyManager(models.Manager)
def get_query_set(self):
'''Only get items that are 'approved' and have a `pub_date` that is in
the past. Ignore the rest.'''
queryset = super(MyManager, self).get_query_set()
queryset = queryset.filter(status__in=('a',))
return queryset.filter(pub_date__lte=datetime.utcnow())
And this works well enough; however, I have a problem using Django's generic.list_detail views object_detail and object_list: the queryset seems to be only loading once and, because of this, it isn't fetching the items it should be because, I assume, the utcnow() time has been called only once (when it first loaded).
I assume this is intentional and meant as a performance boost - however, it means that video's show up elsewhere on the site (in places I am not in a object_detail view) before they are available in an object_detail view (see urls.py below). This is leading to 404s ...
Any ideas ? Or do I have to write my own custom views to avoid this ?
Thanks!
urls.py
url(r'^video/(?P<object_id>\d+)$',
list_detail.object_detail,
{ 'queryset': Video.objects.all(), },
name='video_detail',
),
It is not a problem of cache: as you do it now, the queryset definition is evaluated once, while parsing urls, and then, it is never evaluated again.
Solution is actually pretty simple and described in the online documentation: Complex filtering with wrapper functions: just create a small custom view, that will simply call the generic view.
I am actually using a similar solution quite a lot and I feel it quite comfortable.
By the way, a small side note, for this case I would suggest not using a custom manager, and go back instead on a normal filtering.
Try correcting urls.py to:
url(r'^video/(?P<object_id>\d+)$',
list_detail.object_detail,
{ 'queryset': Video.objects.all, }, # here's the difference
name='video_detail',
)
Edit:
If this fail, try apply similar technique(passing callable instead of calling it) to filter():
return queryset.filter(pub_date__lte=datetime.utcnow)
I have an almost identical model Manager to thornomad, and the same problem with generic views.
I have to point out that neither of the suggestions above work:
doing Video.objects.all without parentheses gives an error
doing queryset.filter(pub_date__lte=datetime.utcnow), again without the parentheses, does not give an error but does not fix the problem
I have also tried another way, which is to use a lambda to return the queryset, eg:
qs = lambda *x: Video.objects.all()
url(r'^video/(?P<object_id>\d+)$',
list_detail.object_detail,
{ 'queryset': qs(), },
name='video_detail',
),
...it didn't work either and I can see now I must have been desperate to think it would :)
lazy_qs = lambda *x: lazy(Post.live_objects.all, QuerySet)
blog_posts = {
'queryset': lazy_qs(),
...doesn't work either (gives an error) as utils.functional.lazy doesn't know how to convert the result to a QuerySet properly, as best I can tell.
I think Roberto's answer of wrapping the generic view is the only one that will help.
The django docs should be amended to point out the limitations of the queryset used by generic views (currently the docs have a special note to tell you everything will be okay!)