django save a form for a user that has not registered yet - django

I need some advice / ideas if someone is inclined to help: I have a javascript interface for manipulating pictures. Basically it's about moving photos around. Once it's done the position of images is saved into a Django form and then saved to database with the owner saved as the current user. Now the trick is that I would like to be able to allow non registered users to play with this interface and then if they like the result they hit save and are redirected to an account registration page and only then the form is actually saved with their user as the owner.
What comes to my mind now is to keep the values of the form in session but I don't know what will happen to the session once the anonymous user registers and becomes another user. I was also thinking of using a 'next' parameter in the registration process with the url filled with get parameters that would be the content of the form but then I don't know if userena is ready to allow that.
Any light on this is welcome.

Well, we did similar thing on our site.
When unregistered user attach photos we save objects to database and assign unique hash which was generated when user came to the page with form. When user hit submit we pass this hash in url and on the next step, when user wants to register, we just get objects from database by this hash and assign user_id to them.
Also we have a cron job which do clean up and removes all lost objects
P.S. Sorry for my english i hope you'll get my point

Save the object without the user and store a reference of that object in the session (or (signed) cookie). If if the user registers, update all the objects with the newly created user.

Another approach would be to store the data in the browser (html5 localstorage and fallbacks, or similar) and only insert it into the database once the user has signed up. You would need to make sure both things happen inside the same browser 'instance', but things would be easier in the server side.

Related

How to store a returned javascript value in a django form?

I want to store things like Browser type and GPS locations for mobile devices along with user input data.
I know how to set up a form in Django so that the user inputs are stored in the database. But how would I go about executing a Javascript function that will a return a value to be stored in the same model as all the user inputtext in the form?
I found reasonable two ways:
I would go for api and do request to server with information you want to store and put it to your model (for instance REST django-rest-framework). But its more complicated since you need to handle authorisation.
As altenative to api you could create hidden fields in form which would be filled by js code executed from the browser. When user clicks submit button he sends you data with no need to manually provide it.

Django: How to trigger a session 'save' of form data when clicking a non-submit link

I know if want you to store form information to the session during a submit you do it with the 'def post' in your view. However, I can not figure out how to store form information when a random link e.g. 'homepage'.
How would you go about storing form information when a user clicks away from the form?
To store information in the session, you don't really need to post, or submit some kind of form. You can do it anywhere, where you have request using session attribute.
the session is dict like object and you can save there any basic types (str, int, float) if you use django's basic configuration, like so:
request.session["data1"] = "my data stored in session"
request.session.get("data2")
also, please note that you may directly have no session accessible, since django won't automatically create session for you. in fact to resolve the issue you could "initialize" it, with: request.session.save()
please take a look at official documentation.

Django: what are some good strategies for persisting form input across login?

I have a webapp that allows authenticated as well as anonymous users to start entering some form data. If a user is happy with his/her input, he/she can save that form to the server. This is a very similar problem to a shopping cart application that does not require login until checkout time.
For the authenticated user, implementing a save button is trivial. However for the anonymous user, the form data need to be stored somewhere while authentication is taking place, then correctly retrieved after logged in. Can someone please suggest some general strategies to go about this?
I found this link that is promising but I want to be thorough about this topic.
I think the correct way of doing this is to use django sessions. Basically each user (anonymousUser included) has a session during its stay on the website (or even more).
If you have a form that you want to store for a specific session, you can do it by using
request.session['myform'] = form
you get it by
request.session['myform']
and you can delete it using
del request.session['myform']
Basically Django pickles a dictionary of the session and saves it in a place (typically the database, but can be on other place as explained in django sessions).

Django Authentication: Creation of objects by users before registration

I am working on a Django site where people create articles. I'd like for people to be able to create an article as part of the registration process. Here's the steps:
User hits "create article" without being registered or logged in.
User is directed to "create article" page that displays the form for creating the article.
After hitting the "submit" button on the "create article" form, the user is redirected to the registration / login page.
After the registration process or login, the article is saved under the user's ID.
I'm pretty new to Django, so here are the complications so far as I'm concerned:
Do I save the object with an AnonymousUser as the author until after the login process? How would I find the object again so that I can save it to the User after they're logged in or registered? Is there any kind of unique identifier in an AnonymousUser object?
Should I pass the object through the registration process using URLs until the registration has taken place (to then save it)? How does one do that?
There are a couple of ways to do what you're wanting to do. I would exclude the user from your create Article form, and set user to blank=True, null=True.
It's really up to you as to whether or not you just hold the article in session until after you create your user, or persist it to the database and assign the user after.
One benefit of holding it in session is that if the user abandons the registration process, you don't have a record in the database. I would recommend going this way, as it's easy to do, and you don't have to have any logic to clean up your db, should the user abandon the session.
To specifically answer your question about an anonymous user...no, there is not a unique identifier for an anonymous user. You can use sessions in Django to persist objects between views.
"Should I pass the object through the registration process using URLs until the registration has taken place (to then save it)? How does one do that?"
The above suggestion that you have been made is the better solution but don't pass it to url. There are two ways to successfully do that.
You can pass the object through session variable so that no one will ever notice it instead of passing it to url.
You can determine which object you must get throught their IP address.

In Django, what is the right place to plug in changes to the user instance during the login process?

Background
I have a custom authentication back end for our django applications that refers to an LDAP server.
As soon as I authenticate someone, I have a wealth of information that our network infrastructure guys put in the LDAP server about the user - their last names (which can change, for instance, if they marry), their e-mails (which can also change), plus other company specific information that would be useful to transfer to the Django auth_user table or profile table for local reference. (*)
To take advantage of this data, as of now, in our custom authenticate method I'm looking up (if it is an existing user logging in) or creating a new (if a new user that never logged in to our Django apps) user, making any changes to it and saving it.
This smells bad to me. Authentication should be about saying yay or nay in granting access, not about collecting information about the user to store. I believe that should happen elsewhere!
But I don't know where that elsewhere is...
My current implementation also causes a problem on the very first login of a user to one of our Django apps, because:
New user to our apps logs in - request.user now has a user with no user.id
My custom authenticate method saves the user information. Now the user exists in the DB
django.contrib.auth.login() kicks in and retrieves the request.user (which still has no user.id and no idea that authenticate saved the user) and tries to save an update to last logged in date.
Save fails because there is already a row in the database for that username (unique constraint violation)
Yes, this only happens the very first time a user logs in; the next time around it will be an update, request.user will have a user.id and everything is fine.
Edit: I'm investigating the striked-out area above. The login code clearly only uses the request.user if the user is None (which, coming out of the validation of the AuthenticationForm it shouldn't be. I probably am doing something wrong in my code...
But it still smells bad to have the authentication doing more than just, you know, authenticating...
Question
What is the right place to plug in changes to the user instance during the login process?
Ideally I would be able to, in my custom authenticate method, state that after login the information collected from a LDAP server should be written to the user instance and potentially the user profile instance.
(*) I do this local caching of the ldap information because I don't want to depend on it being up and running to let users log in to my systems; if ldap is down, the last username and password in auth_user are accepted.
I've done similar things by writing my own authentication backend and putting it in the authenticate() method. The code is public and up here. I also included a pluggable system of "mappers" to do most of the work that isn't just authenticating the user (eg, getting fullname from ldap, automatically creating groups based on "affiliations" that our auth service gives us, and mapping certain users and affiliations into staff/superuser roles automatically).
Basically, the authenticate method looks like:
def authenticate(self, ticket=None):
if ticket is None:
return None
# "wind" is our local auth service
(response,username,groups) = validate_wind_ticket(ticket)
if response is True:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = User(username=username, password='wind user')
user.set_unusable_password()
# give plugins a chance to pull up more info on the user
for handler in self.get_profile_handlers():
handler.process(user)
user.save()
# give plugins a chance to map affiliations to groups
for handler in self.get_mappers():
handler.map(user,groups)
return user
else:
# i don't know how to actually get this error message
# to bubble back up to the user. must dig into
# django auth deeper.
pass
return None
So I pretty much agree with you that authentication should be just a yes/no affair and other stuff should happen elsewhere, but I think with the way Django sets things up, the path of least resistance is to put it in with authentication. I do recommend making your own authentication code delegate that stuff to plugins though since that's within your control.
I'm only fetching the LDAP data on their very first login though (when the auth_user row gets added). Anytime they login after that, it just uses what it already has locally. That means that if their LDAP info changes, it won't automatically propagate down to my apps. That's a tradeoff I'm willing to make for simplicity.
I'm not sure why you're running into problems with the first login though; I'm taking a very similar approach and haven't run into that. Maybe because the login process on my apps always involves redirecting them to another page immediately after authentication, so the dummy request.user never gets touched?
This will be a two part answer to my own question.
What is the right place to plug in changes to the user instance during the login process?
Judging from the Django code, my current implementation, and thraxil's answer above, I can only assume that it is expected and OK to modify the user instance in a custom authenticate() method.
It smells wrong to me, as I said in my question, but the django code clearly assumes that it is possible that a user instance will be modified and I can find no other hooks to apply changes to the user model AFTER authentication, elsewhere.
So, if you need an example, look at thraxil's code - in the selected answer to my question.
Why my implementation is working differently from thraxil's and generating a unique constraint violation?
This one was rather nasty to figure out.
There is absolutely nothing wrong with Django. Well, if it already supported multiple databases (it is coming, I know!!!) I probably wouldn't have the problem.
I have multiple databases, and different applications connect to one or more different ones. I'm using SQL Server 2005 (with django_pyodbc). I wanted to share the auth_user table between all my applications.
With that in mind, what I did was create the auth models in one of the databases, and then create SQL Server synonyms for the tables in the other databases.
It works just fine: allowing me to, when using database B, select/insert/update/delete from B.dbo.auth_user as if it were a real table; although what is really happening is that I'm operating on A.dbo.auth_user.
But it does break down in one case: to find the generated identity, django_pyodbc does a:
SELECT CAST(IDENT_CURRENT(%s) as bigint) % [table_name]
and that doesn't appear to work against synonyms. It always returns nulls. So when in my authenticate() method I did user.save(), the saving part worked fine, but the retrieval of the identity column didn't - it would keep the user instance with a id of None, which would indicate to the django code that it should be inserted, not updated.
The workaround: I had two choices:
a) Use views instead of synonyms (this is what I did)
b) Reload the user right after a user.save() using User.objects.get(username=username)
Hope that might help someone else.