Skip to content

Instantly share code, notes, and snippets.

@sneeu
Created May 22, 2012 12:15
Show Gist options
  • Save sneeu/2768694 to your computer and use it in GitHub Desktop.
Save sneeu/2768694 to your computer and use it in GitHub Desktop.
def beta_access(request):
if request.method == 'POST':
form = BetaCodeForm(request.POST)
# form validation handles checking for the beta code
if form.is_valid():
request.session['has_beta_access'] = True
return redirect('register')
else:
form = BetaCodeForm(request.POST)
return TemplateResponse(request, 'beta_access.html', context={'form': form})
def beta_access_required(view):
def f(request, *args, **kwargs):
if request.session.get('has_beta_access', None) != True:
return redirect('beta_access')
return view(request, *args, **kwargs)
return f
@pyrat
Copy link

pyrat commented May 22, 2012

Python newbie question: What does **kwargs with the 2 * mean as opposed to one?

@sneeu
Copy link
Author

sneeu commented May 22, 2012

@pyrat: in the definition, args (with one *) is a tuple of positional arguments, kwargs (with two *s) is a dictionary of named arguments. In a function call, they can be used to expand a tuple (or list), and/or a dict of paramters into arguments.

For example:

def something(a, b, c):
    # Do something

Can be called:

something(1, 2, 3)
something(*[1, 2, 3])
something(**{'a': 1, 'b': 2, 'c': 3})

Why you want to do this is another lesson ;)

@judy2k
Copy link

judy2k commented May 22, 2012

@pyrat - ** means the argument is a dictionary of any provided named parameters. With a single *, the argument is a list of indexed parameters.

def my_func(*args, **kwargs):
    print args, kwargs

my_func(100, 300, name='Bob', age=12)

would print:

[100, 300] {'name': 'Bob', 'age'=12}

@judy2k
Copy link

judy2k commented May 22, 2012

Dammit, sneeu beat me with a better-formatted answer ;)

@sneeu
Copy link
Author

sneeu commented May 22, 2012

@bedmondmark only just—updated your comment to have better formatting too.

@pyrat
Copy link

pyrat commented May 23, 2012

Thanks. Seems like a nice feature.. the alternative being passing a hash / object to a function instead for named parameters.

So can I have the lesson: Why would you want to do this?

@sneeu
Copy link
Author

sneeu commented May 23, 2012

@pyrat there are quite a few reasons, Jinja2 uses the keyword arguments to define the context in a template.

In the example above, we’re defining a decorator, which is used:

someview = beta_access_required(someview)

The original someview function is wrapped by the beta_access_required decorator, which in the example replaces it with f (which has a closure over view, so the original view can be called from within).

But the beta_access_required function doesn’t really care about the arguments, so it can pass them straight through, image having to write a different beta_access_required for every view function signature.

Django’s user_passes_test decorator (which is what login_required is) is similar.

Python also has the syntactic sugar:

@beta_access_required
def someview(request, some, arguments):
    pass

Which is the same as the above.

@sneeu
Copy link
Author

sneeu commented May 23, 2012

@pyrat additionally, in Django if you wanted to do something before saving a model, you overwrite the save method, and eventually call super, generally you’re not bothered about what save is called with, so you’d use *args, and **kwargs in that instance too.

An example:

class TimestampedModel(models.Model):
    last_updated = models.DatetimeField(blank=True, null=True)

    def save(self, *args, **kwargs):
        # Not too bothered about what args are used, just throw them up the inheritance tree.
        self.last_updated = datetime.datetime.utcnow()
        super(TimestampedModel, self).save(*args, **kwargs)

@pyrat
Copy link

pyrat commented May 24, 2012

Nice, thanks for the rundown. Hopefully I will find an excuse to write some Python in the near future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment