tags:

views:

561

answers:

11

I want to have an input element at the to of my webpage that contains some global settings for a logged in user. How could I attach this piece of data to every single GET/POST and AJAX request that comes in from my web application? I wanted to tack this onto an existing site that has a lot of code already.

Cookies aren't what I'm looking for, because you couldn't have two different cookies for the same site in different tabs/windows of the same application (an HTML input could have different values for the same logged in user in two different tabs/windows.)

UPDATE: To clarify, the feature I want is to bridge two different modes in the same browser and let the user switch between the two modes via an option menu. The current mode would affect everything the user does even down to short/small Ajax requests (every request into the system would need to know what mode the browser window is in.) So two different tabs in the same browser would behave very differently from each other if they are in different modes.

+5  A: 

Sounds like a classic case of hidden fields. You'd need some way of knowing who the user is when you send the HTML out, obviously. NB this is in no way secure at all, the user could potentially send whatever data they wanted.

<input type="hidden" name="fieldname" value="fieldvalue" />
Joe
If you chose this route you'd need to use javascript to make sure that any links which are clicked also submit the form.
Mikey
How could I make sure this input is attached to every single web request? What about an AJAX request that is tied to some small part of the screen?
MikeN
I was assuming you can't do this with sessions (which are usually tied to cookies). If you can use sessions, you should. They're pretty much the only way you're going to remember who your client is (without a lot of pain re-inventing the wheel).Sessions can also be done with URL re-writing, no cookies required (depending on your framework). As Byron Whitlock says, passing variables around certainly isn't the 'correct' way of keeping track of people. And if you have the session and store this info there, there's no need to pass data back and forward through forms.
Joe
+1  A: 

You could use a template tag and template inheritance.

Hank Gay
+2  A: 

Why aren't you using sessions for persisting user data? With a session, you can persist the data across posts, keep session data depending on the page.

Byron Whitlock
A session wouldn't keep track of two different open browser windows or tabs having a different state.
MikeN
Sure you can, just check the referrer, and store session data based on that. You would need to create some sort of click stream data to handle transitions form one page to another. Again, this will be more robust than trying to persist form variables.
Byron Whitlock
referrer is not reliable.
Chii
A: 

Why dont you use a custom template render method ?

def myrender(request, model, data={}):
    from django.template import RequestContext
    from django.shortcuts import render_to_response
    data['user'] = request.user
    return render_to_response(model, data, context_instance=RequestContext(request))

Then create a templatetag to render this in your pages ?

I use this technique to display login info and current page

jujule
How does this work with links and AJAX requests?
Byron Whitlock
you can also use this to add data to your ajax responses. i dont understand what you mean by 'links'.
jujule
RequestContext by itself contains data that are added by sitewide-definable "context processors", so it would be sufficient to use that and then just add context_instance=RequestContext(request) to render_to_response().
che
+3  A: 

What about making different subdomains for your 2 different models?

Kane
A: 

This may be totally useless, depending on what your 'modes' are:

What if the server returns the same page every time, and the modes are applied by javascript. The JS could alter styles and DOM, or issue differing AJAX requests, depending on the mode.

grossvogel
A: 

This can elegantly be accomplished with jquery.Metadata. You'd simply need to push a form on each page with one element containing all your data that you'll append to each form once it's getting submitted:

On each page you would have:

<form name="_settings" type="post">
    <p>
        <input name="_settings" type="hidden" value="{preference: 'chocolate', 
             displayListSize: 30, something: 'else'}" />
    </p>
</form>

Now you can do something with similar fashion:

// Bind to the submit event of each form
$('form').submit(function () {
    // Append all input fields from `_settings`
    $(this).append('form[name="settings"]:input');

    return true;
});

Or append the settings to a specific form, or all forms if none specified:

function appendSettings(form) {
     if (form)
         $(form).append('form[name="_settings"]:input');
     else
         $('form').append('form[name="_settings"]:input');
}

...after which you can submit the form as part of an AJAX request (that may not have triggered the forms submit event otherwise)

EDIT:

As for GET/POST request via links:

$('a').click(function(event) {
    href = $(this).attr('href');

    event.stopPropagation();
    event.preventDefault();

    if (!href.match('?'))
        // Change the form's action to the link's reference
        $('form[name="_settings"']).attr('action', href).submit();
    else {
        /* Sanitize the form's data into GET paramaters and
           change the window's location*/
    }   
});

If you wouldn't like to use jquery.Metadata, you can make as many hidden fields with name/value pairs on the _settings form and it would still work. I've used it as a good example of portable data since both JS and Django can speak JSON.

Also, I'm assuming too much by writing a jQuery example, but I'm somewhat hoping that your already using some framework and that this could neatly be translated to the framework of your choice.

kRON
I am using jquery and this sounds like a great start. Is there a way I could attach this metadta to small AJAX requests?
MikeN
`$.ajax({url: "/some-page", data: {some: "data", other: "data", _settings: $('form[name="_settings"]:input').metadata()}, success: handleResponse});`
kRON
A: 

You can create a custom context processor and use it with RequestContext to add some custom data for your generic template (and then use inheritance to get this to all other templates, as Hank Gay says).

che
+11  A: 

If I understand correctly, your app has two modes. Let's call them red and blue. User opens window 1 and selects red. User then opens window 2 and selects blue. If you merely used sessions, then if the user goes back to window 1 and clicks anything, the result would be blue because the previous click was blue, even though window 1 is red.

I suggest using mod_rewrite (or an equivalent on non-Apache servers) to modify the URL to indicate the mode.

Incoming request for

example.com/red/yourscript  ==>  example.com/yourscript?mode=red
example.com/blue/yourscript  ==> example.com/yourscript?mode=blue

If all the links and forms on your page are relative and not absolute, the URLs should have the mode info in them.

That is, if the page URL is

example.com/red/yourscript

then links on the page that look like this

<form action="anotherscript">

will have a URL of

example.com/red/anotherscript

Another way would be to use subdomains

red.example.com/yourscript  ==>  example.com/yourscript
blue.example.com/yourscript  ==> example.com/yourscript

You would not need mod_rewrite if you configure your server to serve both subdomains from the same real location.

Using the subdomains would allow you to extract the mode from the http_referer field of each incoming request and you wouldn't need to add the query string.

Lastly, add absolute links to explicitly change the mode.

<a href="http://example.com/red/changemode"&gt;Click here to go to RED mode</a>
<a href="http://example.com/blue/changemode"&gt;Click here to go to BLUE mode</a>
bmb
+1 This is a great idea.
Byron Whitlock
This approach will only work if the Web Application is stateless, i.e. each HTTP request is atomic and causing a request to be repeated will not break the application. If you application is stateful this won't work.
Martin Spamer
Martin Spamer, yes, it will work even if the application is stateful.
bmb
A: 

I have an application which stores the entire session this way right now, I've introduced specialized ajax wrappers for all of my server calls.

$.myget = function (url, data, k) { $.get(url, $.extend(read_session_data(), data), k); }

read_session_data is left as an exercise for the reader.

In addition to managing this issue, it make it easier for me to handle errors in a controlled (and concise) manner.

Lang Martin
+1  A: 

What you are trying to sounds a bit like this stackoverflow question I answered.

You can do changes like this in middleware (see the link above) but it doesn't really make sense. It's more likely that you want to change the session data.

Following are the different options I see that you have.

1) GET

Changing the GET data would effectively refirect the user as it changes the address in the URL and this sends the user to a new page. So, you can edit this but it will redirect the user each time they load a page! Bad idea. Otherwise you could somehow manage your URL's so each link adds something in but it still doesn't really make sense as a user thats not logged in could add this to the URL if they know what it is. Again, bad idea.

2) POST

The post information is what is sent to you from the browser, besides cleaning the data its not really the right place to add and replace information.

3) SESSION

You can find out about Django sessions here. This is a technique used in a number of places, in particular in django.contrib.auth. The session is used to verify the user and modify the request object. This may be what you want to do, here you could either modify the session or change the request object - however, I'd be wary of making too many changes to request as it could cause conflicts with other apps or future django changes/additions.

4) jQuery (since you tagged this as jQuery) too

You could do something hacky and nasty like this in javascript, if you insist.

$("a").each(function()
   { 
      this.href += "?newdata=1";
   });

However I wouldn't recommend this and it doesn't take into account there already being GET data (if there is). I just add it to show you another option as I'm not quite sure what your trying to do.

Orange Box