Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Pollyanna
Mar 5, 2005

Milk's on them.


I'm at the point where I want to handle editing and deleting object models from the user side. I get how to create and read objects from the back-end, but is there a Django-ish way to edit and delete things from the front-end?

Also, I'm deploying on Heroku, and I keep running into problems with the database. I use SQLite on my end, but Heroku needs PostgreSQL. I added the Heroku database and wsgi settings, and it works fine on Heroku, but running it locally with Foreman gives me this issue:

code:
ImproperlyConfigured at /tracker/
settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
when I try and check if a user is authenticated. Why does this happen? It works online, so why doesn't it work locally?

Plus, I thought Heroku was supposed to handle the migration from SQLite to PostgreSQL itself, so I shouldn't have to change settings myself?

Adbot
ADBOT LOVES YOU

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb

MonkeyMaker posted:

Wait, switching on DEBUG changed the way it acts?

Can you show the entire model (at least the methods of the model) and the model's custom admin if it has one?

Yup, that appears to be the case, as strange as it may be. Unfortunately, I can't share the whole model and methods. Your question about the custom admin has got me thinking though - this one is an MPTT model, and that does custom admin stuff for rendering the hierarchy picklist options. I wonder if that's the culprit. Don't see any reference to the DEBUG setting in there, so doesn't really explain that behavior, but I'm gonna play around with it some more.

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb

Pollyanna posted:

I'm at the point where I want to handle editing and deleting object models from the user side. I get how to create and read objects from the back-end, but is there a Django-ish way to edit and delete things from the front-end?

Also, I'm deploying on Heroku, and I keep running into problems with the database. I use SQLite on my end, but Heroku needs PostgreSQL. I added the Heroku database and wsgi settings, and it works fine on Heroku, but running it locally with Foreman gives me this issue:

code:
ImproperlyConfigured at /tracker/
settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
when I try and check if a user is authenticated. Why does this happen? It works online, so why doesn't it work locally?

Plus, I thought Heroku was supposed to handle the migration from SQLite to PostgreSQL itself, so I shouldn't have to change settings myself?

Your CRUD operations (create, read, update, delete) always happen on the back-end. The only way for somebody to do it through the front should be via the back-end (:butt:). The Django way of doing this is through forms.

It sounds like you may need two settings.py files, one that heroku will use, and one that you will use locally. My settings.py is all setup for the server to use, but at the bottom of it I have:
code:
try:
    from settings_local import *
except ImportError, e:
    pass
Then locally I create a settings_local.py file which contains:
code:
DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3'
    }
}
That allows me to override some of the things I defined in settings.py.

I do not check in settings_local.py to source control or deploy it anywhere, it only needs to be used on my local machine.

fletcher fucked around with this message at 22:44 on Feb 25, 2014

NtotheTC
Dec 31, 2007


fletcher posted:

I do not check in settings_local.py to source control or deploy it anywhere, it only needs to be used on my local machine.

If I may sing the praises of Two Scoops of Django for a moment, they have a very good section on why you should avoid the settings_local.py anti pattern. (the short version is: unchecked-in code means it can be lost, also means all team members will have different settings_local.py files which leads to "It doesn't do that for me" errors that take hours to debug.)

Instead, have a settings folder in your project that contains all your different settings_*.py files (i.e. production, testing, development) and check them all in. To avoid checking in sensitive information like SECRET_KEY, leave it out of settings_production.py and instead pass it in via an environment variable on the server. In your settings_development.py you can have a throwaway SECRET_KEY variable that you do not use in production just to stop you having to faff around with it.

This won't really affect you when you're working alone but it's a good habit to get into.

Pollyanna
Mar 5, 2005

Milk's on them.


Why does this happen, though? I tracked it down to calling dj_database_url.config(), which tries to configure a database by looking for an environmental variable in my system. Turns out that even though Heroku configured a DATABASE_URL for me, at least according to heroku config, $DATABASE_URL doesn't actually return anything:

code:
pollyanna$ echo $DATABASE_URL

pollyanna$ gently caress
-bash: gently caress: command not found
Passing in the string that is supposed to be the DATABASE_URL as an argument doesn't work, either. I still get an empty configuration dictionary. That at least should work. What's the deal?

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb

NtotheTC posted:

If I may sing the praises of Two Scoops of Django for a moment, they have a very good section on why you should avoid the settings_local.py anti pattern. (the short version is: unchecked-in code means it can be lost, also means all team members will have different settings_local.py files which leads to "It doesn't do that for me" errors that take hours to debug.)

Instead, have a settings folder in your project that contains all your different settings_*.py files (i.e. production, testing, development) and check them all in. To avoid checking in sensitive information like SECRET_KEY, leave it out of settings_production.py and instead pass it in via an environment variable on the server. In your settings_development.py you can have a throwaway SECRET_KEY variable that you do not use in production just to stop you having to faff around with it.

This won't really affect you when you're working alone but it's a good habit to get into.

This seems like a great idea, but I guess I should mention the other piece of my puzzle: Chef. I already have all my environments defined in Chef (prod, testing, dev, etc), and they all use the same settings_local.py.erb template file. So while my settings_local.py doesn't come from the same repo as my django app, it does come from a central repo and all devs would have the same copy of it. Not saying this is a great setup, but that's why the settings_local.py pattern appealed to me in the first place.

MonkeyMaker
May 22, 2006

What's your poison, sir?

Pollyanna posted:

Why does this happen, though? I tracked it down to calling dj_database_url.config(), which tries to configure a database by looking for an environmental variable in my system. Turns out that even though Heroku configured a DATABASE_URL for me, at least according to heroku config, $DATABASE_URL doesn't actually return anything:

code:
pollyanna$ echo $DATABASE_URL

pollyanna$ gently caress
-bash: gently caress: command not found
Passing in the string that is supposed to be the DATABASE_URL as an argument doesn't work, either. I still get an empty configuration dictionary. That at least should work. What's the deal?

Heroku configured DATABASE_URL on Heroku, not on your personal system.


dj_database_url() also takes arguments for what to do when DATABASE_URL isn't found:

code:
DATABASES = {'default': dj_database_url.parse('postgres://...')}

Pollyanna
Mar 5, 2005

Milk's on them.


MonkeyMaker posted:

Heroku configured DATABASE_URL on Heroku, not on your personal system.


dj_database_url() also takes arguments for what to do when DATABASE_URL isn't found:

code:
DATABASES = {'default': dj_database_url.parse('postgres://...')}

Oh. That might explain it. But why not? Django is still gonna want to use $DATABASE_URL.

And I tried the default argument by passing in the postgres string, but it didn't work at all for me.

MonkeyMaker
May 22, 2006

What's your poison, sir?

Pollyanna posted:

Oh. That might explain it. But why not? Django is still gonna want to use $DATABASE_URL.

And I tried the default argument by passing in the postgres string, but it didn't work at all for me.

Because Heroku can't won't change things on your local system?

You'll want to pass in a SQLite connection string and not a Postgres one since you're using SQLite locally and not Postgres.

Pollyanna
Mar 5, 2005

Milk's on them.


I'll do the whole "comment out while working locally" deal for now. I have some other bugs to fix...

For example, I'm trying to save changes to an object like this:

Python code:
@login_required(login_url='/tracker/login/')
def edit(request, app_id):

    user = request.user
    app = Application.objects.get(pk=app_id)

    print app.submitted_date

    if app.user == user:

        if request.method == 'POST':

            form = ApplicationForm(request.POST, instance=app)

            if form.is_valid():

                form_data = request.POST

                print form_data

                app.title = form_data['title']
                app.description = form_data['description']
                app.company = form_data['company']
                app.submitted_date = form_data['submitted_date']

                app.save()

                return HttpResponseRedirect('/tracker/user/{0}/'.format(user.username))

            else:

                print form.errors

        else:

            form = ApplicationForm(instance=app)

    else:
        return HttpResponse('User mismatch, permission denied.')

    content = { 'form': form, 'app': app }

    return render(request, 'tracker/edit.html', content)
Now, I ran into the problem where you can't go form.save() because that will simply add a new app rather than update the old one. Problem is, the datepicker breaks, since it writes things to the submitted_date field as MM/DD/YYYY and the database only uses YYYY-MM-DD. I can fix it by choosing a new date, but then the database complains about validation. I solved this problem by adding default=date.today().strftime("%m/%d/%Y") to the submitted_date field, and it works for the addapp function, but not for the edit function. By this point it's gotten complicated and weird, and I'm a bit lost. Is there a better way to do this?

MonkeyMaker
May 22, 2006

What's your poison, sir?
I'm assuming "ApplicationForm" is a modelform based on the Application model?

If so, you don't need to do all this work of updating things on `app` and then saving it. Just do `form.save()` if form is valid.

Django should handle converting the dates for you but if it doesn't you can always reset the date with strftime after you create a datetime object with strptime. Something like:

code:
if app.user == user:
    form = ApplicationForm(request.POST or None, instance=app)  # save yourself some typing

    if request.method == 'POST':
        if form.is_valid():
            obj = form.save(commit=False)
            obj.submitted_date = datetime.datetime.strptime(form.cleaned_data['submitted_date'], '%m/%d/%Y')
            obj.save()
        else:
            # handle invalid form here
    else:
        # handle non-POST here
else:
    # handle different user here.

Pollyanna
Mar 5, 2005

Milk's on them.


I'm still confused as to why when I add an app the correct datetime format shows up, but when the app loads during editing it's in YYYY-MM-DD :( And now it gives me a "must be string, not datetime.date" error. Argh.

I just bit the bullet and changed the datepicker around instead. Thanks for the help!

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

I haven't done any Django work in months and I find I've forgotten half of everything.

I've got a very simple model with two fields. The user shouldn't actually be typing in the data for those fields, my code will take what they submit, do stuff, and then create the data for those fields.

I think what I would have done before I forgot half of everything is use a CreateView and ModelForm and override...form_valid, fields attribute to hide the model fields, and add my own fields. Am I remembering that right?




In other news, I've been playing around with django-allauth for user registration and social network registration/login/auth/whatever. It seems to work very nicely, so I guess consider this if you need to do the whole login-with-your-facebook/google/github/whatever-account.


In other, other news, I got my copy of Two Scoops of Django for Django 1.6 a couple days ago since I'm getting ready to do some Django work again. A lot of expanded content compared to the version for 1.5 as well as adding stuff specific for 1.6. In my opinion, worth buying even if you have the last version for 1.5. Unfortunately, they no longer have an ebook version...

One thing they've changed their tune on is they no longer recommend using SQLite for local dev. I don't blame them and I've been using PostgreSQL locally since not long after I started doing any Django work. It's pretty simple to set up PostgreSQL no matter your OS.

Thermopyle fucked around with this message at 21:30 on Mar 2, 2014

MonkeyMaker
May 22, 2006

What's your poison, sir?

Thermopyle posted:

I haven't done any Django work in months and I find I've forgotten half of everything.

I've got a very simple model with two fields. The user shouldn't actually be typing in the data for those fields, my code will take what they submit, do stuff, and then create the data for those fields.

I think what I would have done before I forgot half of everything is use a CreateView and ModelForm and override...form_valid, fields attribute to hide the model fields, and add my own fields. Am I remembering that right?

Just mark the fields as uneditable with `editable=False`. This'll hide the fields from any model forms (and the admin).

If, however, you need to be able to edit the fields in the admin, or in random forms, but not in all forms, then, yes, just hide the fields in the form's __init__ or fields tuple in class Meta.

You shouldn't need to override form_valid. If you need the fields to be calculated when the form is saved, do that in the form's save().

Thermopyle posted:

In other, other news, I got my copy of Two Scoops of Django for Django 1.6 a couple days ago since I'm getting ready to do some Django work again. A lot of expanded content compared to the version for 1.5 as well as adding stuff specific for 1.6. In my opinion, worth buying even if you have the last version for 1.5. Unfortunately, they no longer have an ebook version...

One thing they've changed their tune on is they no longer recommend using SQLite for local dev. I don't blame them and I've been using PostgreSQL locally since not long after I started doing any Django work. It's pretty simple to set up PostgreSQL no matter your OS.

Yeah, TSoD1.6 is a great update to 1.5. I completely understand why they didn't do an e-book, too. I know a lot of people are upset but people are always going to be upset so...gently caress 'em. Do what makes your life better.

And, yes, don't use SQLite.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!
Hello,

I am having trouble trying to figure out how to test a set of multiple forms being submitted sequentially. I tried using FormWizard which works fine when I test manually in the browser, my session variables pass intact and I can move through multiple forms and do my transaction processing (ie. the FormWizard 'done' method launches and performs this work)

But when I try to run it via the testing functions, I can POST multiple forms sequentially but they never seem to be on the same session or kick off the 'done' method. Is there something I am missing to testing multiple sequential form POST requests?

test code (in this case the second step should be omitted via business logic in condition_dict:

code:
    def test_admin_create_user(self):
        resp = self.c.get(reverse('admin_create_user'))
        resp = self.c.post(reverse('admin_create_user'),{
            'admin_reg_user_wizard-current_step': '0',
            '0-CommentLog':'No Comment'
        })
        resp = self.c.post(reverse('admin_create_user'),{
            'admin_reg_user_wizard-current_step': '2',
            '2-ContactTitle':'2',
            '2-ContactType':'1',
        })
I also noticed that the methods in condition_dict in URLS.py do not get called when I run the test POST, but they call fine when manually testing in the browser.

Pollyanna
Mar 5, 2005

Milk's on them.


How do I change permissible symbols in a username using the default (contrib.auth) User model? Or should I allow my users to name themselves crap like +_+_+_+_+_+_+_+_+_+_+_+_+?

edit: Also, I've been told to make sure that user input doesn't contain any malicious code. I was under the impression that Django checks this for me. Or should I employ a different strategy to take care of that?

Pollyanna fucked around with this message at 21:50 on Mar 3, 2014

MonkeyMaker
May 22, 2006

What's your poison, sir?

Pollyanna posted:

How do I change permissible symbols in a username using the default (contrib.auth) User model? Or should I allow my users to name themselves crap like +_+_+_+_+_+_+_+_+_+_+_+_+?

edit: Also, I've been told to make sure that user input doesn't contain any malicious code. I was under the impression that Django checks this for me. Or should I employ a different strategy to take care of that?

Does it matter if they give themselves that ridiculous username? If not, don't worry about it. You can't change the default user but you could sub-class it and change the username's validator if you really need to.

As for malicious input, Django protects against most of it.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!
Nevermind my question above. I had some bad data in a form and figured out how to check for it in the tests.

So far, I'm a big fan of FormWizard.

MonkeyMaker
May 22, 2006

What's your poison, sir?

Ahz posted:

Nevermind my question above. I had some bad data in a form and figured out how to check for it in the tests.

So far, I'm a big fan of FormWizard.

FormWizard is OK if you use it pretty straightforward. If you need to do anything tricky, though, it quickly turns to poo poo.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!

MonkeyMaker posted:

FormWizard is OK if you use it pretty straightforward. If you need to do anything tricky, though, it quickly turns to poo poo.

I think I have it rigged up for most of my key business logic including dynamic fields, add/remove forms in the process, as well that messing around with preview and validation seems to work nicer than I expected. The built-in hooks seem to handle what I think I might need.

But I'm curious what kinds of things you would consider tricky? I'm still open to new ideas as I develop.

Maluco Marinero
Jan 18, 2001

Damn that's a
fine elephant.
Anyone got some trip reports on doing Long Polling in Django? I'm making an offline web-app that syncs using a REST API, and I'd like to try it out.

I don't necessarily need to though, I could just as easily have a rest API endpoint for changes and request periodically with a ?since parameter -- as it isn't mission critical to be real time -- but I'd like to explore long polling if its workable in Django.

ephphatha
Dec 18, 2009




A few pages back I recall a discussion about not using local_settings files for sensitive settings you don't want exposed via source control. I've got an opportunity to rebuild my server (cause the image got wiped...) and currently I've got a basic shell script that runs through installing all the packages I require then uses sed to build a local settings file from a template. I'm working on making it into a collection of fabric tasks and I'm wondering if it's worth replacing the local_settings file with something else? I assume I can use fabric to define environment variables that will persist through reboot cycles (making/editing the root .bashrc?). Would that be preferable?

ufarn
May 30, 2009
Store your on-server production settings in environment variables. local_settings is fine for local development, as long as you add the file to .gitignore, so people can't steal your e-mail settings, etc.

ephphatha
Dec 18, 2009




I have a vm I use for development that I try to keep as close to the production server as possible, so I'll set it all up to use settings in environment variables instead. It's mainly the database password and secret key that I want to keep secret anyway

Pollyanna
Mar 5, 2005

Milk's on them.


I'm trying to get Django going with uWSGI, but I run into this error:

code:
unable to load configuration from apptrack.wsgi
The WSGI deployment docs and uWSGI don't seem to explain why this is happening. Is there something extra I need to do to make it work?

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

ufarn posted:

Store your on-server production settings in environment variables. local_settings is fine for local development, as long as you add the file to .gitignore, so people can't steal your e-mail settings, etc.

Two Scoops of Django disagrees. I'll type up their opinions on the matter when I get home.

ManoliIsFat
Oct 4, 2002

Thermopyle posted:

Two Scoops of Django disagrees. I'll type up their opinions on the matter when I get home.

Two Scoops does say to do your secret keys and passwords in environment variables, but ya, their configuration setup is pretty classy. I won't be home tonight but I'll try and get some notes tomorrow if no one beats me to it.

fletcher
Jun 27, 2003

ken park is my favorite movie

Cybernetic Crumb
Currently I'm running collectstatic on each server the app is deployed on, and django-pipeline handles the less & uglifyjs and all that stuff. It can be kinda time consuming though, I was thinking I should do collectstatic on just 1 server, package it up, and then deploy the packaged version. How should I go about doing that?

fletcher fucked around with this message at 19:29 on Mar 11, 2014

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

ManoliIsFat posted:

Two Scoops does say to do your secret keys and passwords in environment variables, but ya, their configuration setup is pretty classy. I won't be home tonight but I'll try and get some notes tomorrow if no one beats me to it.

Yeah, I meant that Two Scoops says to not use a local settings file that you put in .gitignore.

Chapter 5 starts out with this list of best practices:

  • All settings files need to be version-controlled.
  • Don't Repeat Yourself. You should inherit from a base settings file.
  • Keep secret keys safe. They should be kept out of version control.

Section 5.1 is called Avoid Non-Versioned Local Settings.

quote:

We used to advocate the non-versioned local_settings anti-pattern. Now we know better

Their reasons:
  • Ever machine has untracked code.
  • How much hair will you pull out, when after hours of failing to duplicate a production bug locally, you discover that the problem was custom logic in a production-only setting?
  • How fast will you run from everyone when the 'bug' you discoverd locally, fixed and pushed to production was actually caused by cusomizations you made in your own local_settings.py module and is now crashing the site?
  • Everyone copy/pastes the local_settings.py module everywhere. Isn't this a violation of DRY, but on a larger scale?

They recommend a settings structure based upon a talk by Jacob Kaplan-Moss which looks like:
code:
settings/
    __init__.py
    base.py
    local.py
    staging.py
    test.py
    production.py
All of which goes into your VCS. (You should have a requirements.txt for each settings file)

When you call manage.py commands you do it like:

python manage.py shell --settings=twoscoops.settings.local

Or you can set the DJANGO_SETTINGS_MODULE environment variable. (Personally, I set this variable in my virtualenv postactivate script.)

Configuration like SECRET_KEY, AWS keys, and API keys goes in environment variables.

Their reasons for this advice:
  • Keeping secrets out of settings allows you to store all Python code in VCS.
  • Instead of each developer maintainin an easily-outdated, copy-and-pasted version of local_settings.py, everyone shares the same version-controlled settings/local.py
  • System admins can deploy projects without editing Python code.
  • Most PAAS recommend the use of environment variables for configuration and have built-in features for setting and managing them.

They have a lot more advice on the subject, but you should just go buy the drat book. :D

ephphatha
Dec 18, 2009




Is there any way to get an electronic version of that book? They apparently don't have any Australian distributors for print copies so I'd be waiting weeks for a copy to ship unless I wanted to pay triple the price of the book alone on shipping (instead of just double).

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Ephphatha posted:

Is there any way to get an electronic version of that book? They apparently don't have any Australian distributors for print copies so I'd be waiting weeks for a copy to ship unless I wanted to pay triple the price of the book alone on shipping (instead of just double).

http://twoscoopspress.com/pages/two-scoops-of-django-1-6-faq#format-1.6

MonkeyMaker
May 22, 2006

What's your poison, sir?

Ephphatha posted:

Is there any way to get an electronic version of that book? They apparently don't have any Australian distributors for print copies so I'd be waiting weeks for a copy to ship unless I wanted to pay triple the price of the book alone on shipping (instead of just double).

Contact them. They're really good about helping people get copies of the book at affordable price if they can.

Chosen
Jul 11, 2002
Vibrates when provoked.
At work, I do a compromise. I aim for the strict "12-factor"-ness of keeping settings out of VCS, but the fabric script will recommended settings based on the environment or what is already there. I think I prefer this style largely because I don't like attempting to version code with environments.

That said, our fabric and puppet settings are generally kept in a separate repository from the app code.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

This is an odd gap in my knowledge I'm sure, but I've never built any Django sites that do much of anything with user authentication and authorization. I realize this is odd, as probably the vast majority of Django sites do, but anyway...

Say you have a model PizzaRecipe. Users can input their own pizza recipes. What's the Django way of limiting users to viewing and deleting only recipes they create?

I can, of course, add a user field to the PizzaRecipe model, add the currently-logged-in user to the model on save and then override get_queryset or whatever in my views to make sure the authenticated user matches the user field, but...it I feel like I'm overlooking something obvious since this seems like a basic thing that Django would have some pre-packaged way of handling.

Thermopyle fucked around with this message at 22:48 on Mar 13, 2014

MonkeyMaker
May 22, 2006

What's your poison, sir?

Thermopyle posted:

This is an odd gap in my knowledge I'm sure, but I've never built any Django sites that do much of anything with user authentication and authorization. I realize this is odd, as probably the vast majority of Django sites do, but anyway...

Say you have a model PizzaRecipe. Users can input their own pizza recipes. What's the Django way of limiting users to viewing and deleting only recipes they create?

I can, of course, add a user field to the PizzaRecipe model, add the currently-logged-in user to the model on save and then override get_queryset or whatever in my views to make sure the authenticated user matches the user field, but...it I feel like I'm overlooking something obvious since this seems like a basic thing that Django would have some pre-packaged way of handling.

Nope, that's the way to do it.

Ideally you make a mixin that limits the queryset automatically. You could sort of achieve this with UserPassesTestMixin from django-braces, where the test is that the request.user matches self.get_object().user. They'll get a 404 if they try to view a Pizza they don't own. You'll still have to limit the ListView's queryset to just their Pizzas, though.

EDIT:

Or make a custom QuerySet/ModelManager that has a for_user(user) method that limits the queryset automatically for the user that's passed in, an example.

Pollyanna
Mar 5, 2005

Milk's on them.


I did something like this for my app, sorta:

Python code:
def apps(request, username):

    user = User.objects.get(username=username)

    if request.user == user:

        application_list = Application.objects.filter(user=user).order_by('title')

        past_applications = len(Application.objects.filter(user=user).filter(submitted_date__gte=datetime.today()-timedelta(days=30)))

        content = { 'applications': application_list, 'past_apps': past_applications, 'username': username }

        return render(request, 'tracker/apps.html', content)

    else:

        return HttpResponseRedirect('/tracker/')
This piece of code checks to see if the currently logged in user matches the user associated with that page, and lets them see the user's data if they're the same, or redirects the user to the home page if they're not. It's not quite the same as your example, but the idea is that you filter by user looking for the same user as the currently logged in user. It's pretty simple user. Not at all repetitive user.

Incidentally, for restricting parts of the site to people who are logged in, you can use the @login_required decorator from django.contrib.auth.decorators.

Python code:
@login_required(login_url='/tracker/login/')
def addapp(request):

    if request.method == 'POST':

        app = Application(user=request.user)

        form = ApplicationForm(request.POST, instance=app)

        if form.is_valid():
            form.save(commit=True)

            return HttpResponseRedirect('/tracker/user/{0}/'.format(request.user.username))

        else:
            print form.errors

    else:

        form = ApplicationForm()

    content = { 'form': form }

    return render(request, 'tracker/addapp.html', content)

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!

MonkeyMaker posted:

Nope, that's the way to do it.

Ideally you make a mixin that limits the queryset automatically. You could sort of achieve this with UserPassesTestMixin from django-braces, where the test is that the request.user matches self.get_object().user. They'll get a 404 if they try to view a Pizza they don't own. You'll still have to limit the ListView's queryset to just their Pizzas, though.

EDIT:

Or make a custom QuerySet/ModelManager that has a for_user(user) method that limits the queryset automatically for the user that's passed in, an example.

Thats fine for viewing recipes, but for deleting, one way to go would be to put a validator on the form for the recipe id (to delete) matches a corresponding user recipe. Otherwise you could be spoofed into having a user delete any recipe they want regardless of whether they can view it.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!

Pollyanna posted:

I did something like this for my app, sorta:

Python code:
def apps(request, username):

    user = User.objects.get(username=username)

    if request.user == user:

        application_list = Application.objects.filter(user=user).order_by('title')

        past_applications = len(Application.objects.filter(user=user).filter(submitted_date__gte=datetime.today()-timedelta(days=30)))

        content = { 'applications': application_list, 'past_apps': past_applications, 'username': username }

        return render(request, 'tracker/apps.html', content)

    else:

        return HttpResponseRedirect('/tracker/')
This piece of code checks to see if the currently logged in user matches the user associated with that page, and lets them see the user's data if they're the same, or redirects the user to the home page if they're not. It's not quite the same as your example, but the idea is that you filter by user looking for the same user as the currently logged in user. It's pretty simple user. Not at all repetitive user.

Incidentally, for restricting parts of the site to people who are logged in, you can use the @login_required decorator from django.contrib.auth.decorators.

Python code:
@login_required(login_url='/tracker/login/')
def addapp(request):

Just be aware it doesn't check if the user is active (in case you ever plan to deactivate users)

MonkeyMaker
May 22, 2006

What's your poison, sir?

Ahz posted:

Thats fine for viewing recipes, but for deleting, one way to go would be to put a validator on the form for the recipe id (to delete) matches a corresponding user recipe. Otherwise you could be spoofed into having a user delete any recipe they want regardless of whether they can view it.

Your delete view should have a restricted queryset from which the item to be deleted is selected from. Again, the custom QuerySet or ModelManager will handle this for you, preventing you from doing more work than you have to or the same work more than once.

Adbot
ADBOT LOVES YOU

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

MonkeyMaker posted:

Your delete view should have a restricted queryset from which the item to be deleted is selected from. Again, the custom QuerySet or ModelManager will handle this for you, preventing you from doing more work than you have to or the same work more than once.

Yeah, this is what I did with a QuerySet, via the OwnershipMixin I wrote to handle all of this.

Now I just slap the mixin on my views and all of this is taken care of.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply