Assigning a "database id" to multiple html ids on a page - django

I will use model.id when referencing the id for the table in the database, and id when referencing the id given to elements in my html.
I have a django project where I am using some hidden form fields (all forms have the same id right now for that hidden field) to house the model.id. This works great as long as the model.id is known when the page is rendered.
I am now attempting to modify the process to work when no model.id is given (ie someone has chosen to create a new instance of my model). As far as the backend goes I have this working. No model.id supplied and the view knows it should give empty forms. At this point I choose not to create a new instance of the model, as I only want to if the user actually enters something in one of the forms.
If the user enters something in a form then the form processing creates a new instance of model and passes the id back to the users browser. What I was attempting to do is use the jquery form plugin to save the return data somewhere hidden, which I would then look at and use val to set all of the hidden fields' ids to the model.id that was returned so the next field/form the user submits will know to write to the model that was just created.
Now looking at this I'm guessing the idea of having multiple elements with the same id is bad, but I really do want them to always be the same and only have the hidden fields there to house that same Model.id on every form on the page.
I tried doing something like follows. However only one of the ids on the page actually got the value assigned. Is there a different way I should be accomplishing this goal? Is there something I should add to make all occurrences of id to be set with something like .val(model.id)? If not, does anyone have any suggestions on how to go about this? Maybe django provides a cleaner way of doing exactly what I'm trying to accomplish?
A response returned from form submission.
<response>
<the_model_id_brought_back>3732</the_model_id_brought_back>
...
<response>
The jQuery code attempting to set all of the "id_in_multiple_places" ids to the model.id returned.
jQuery('#descriptionForm').ajaxForm({
target: '#response',
success: function(data) {
the_model_id = jQuery('#response').find("the_model_id_brought_back").html();
jQuery('#id_in_multiple_places').val(the_model_id);
}
});
To explain why I have multiple forms like this. Forms consist of 1 visible field. Multiple forms are on the page. When a user leaves a field (which means they leave the form as well) I will submit that form to the server. This will allow their data to always be saved even if they stop half way through and throw their computer out a window. They can go to a different computer and pick up where they left off.
Thanks.

Now looking at this I'm guessing the idea of having multiple elements with the same id is bad
It's not only bad, it's impossible. You cannot do this. You can get around this by using classes, which don't have to be unique, but you probably shouldn't.
What you should do, is assign the elements sensible class names, and assign their common ancestor the ID. You can start at that element and traverse downwards to find the sub-elements by class name.

Related

Saving ModelForm progress values to session in Django

I have flow where users can create (model) forms. If form is valid, object gets saved and flow continues, but selection is in multiple pages. I need to keep current state of created object, which cannot be saved before it's completely valid.
One thing I can do is to always pass things around those views in the ModelForm to make sure, that user never loses data, but I also wanna make sure, that if he leaves the flow and comes back, he doesn't lose data, that he already entered previously.
That's why I decided I wanna save all the fields to session.
Is this correct approach?
How would you do this?
Where would you put this session logic?
What's best way of getting the fields from incomplete form to be saved?
Edit:
Please don't give me advice on how to use session, I am talking more about high level logic and architecture than specific implementation.
I should describe my flow a bit more. Model has 3 fields.
normal dropdown (foreign key referencing another model)
textfield
another foreign key, but this time not done by select, but it's own separate page with lots of filters to help user pick the right (foreign) model
Flow is not linear, because user can start in different parts of page.
Sometimes user can go to page, where he has first 2 fields + button "Browse", which takes you to selection page for 3rd field. Then after he selects field there, he comes back.
But sometimes he selects first this field and then comes to screen with 2 remaining fields, where he needs to fill those.
django-formtools offers a great way to do this using Form wizard.
The form wizard application splits forms across multiple Web pages. It
maintains state in one of the backends so that the full server-side
processing can be delayed until the submission of the final form.
More info here https://django-formtools.readthedocs.io/en/latest/wizard.html
to save in session:
request.session["variable_name"] = "value"
to get from session request.session["variable_name"]. sure you can use request.session.get("..") in both too

Keeping form choice list up-to-date in django

I have a form in my admin site where the user is to select a single object from a particular model from a dropdown.
thing_choices = [(x.id, x) for x in Things.objects.all()]
class ThingSelector(forms.Form):
thing = forms.ChoiceField(choices=thing_choices)
If I first add a new Thing object, then go to the page with the selector form, I find that the object does not appear in the dropdown. This is presumably the form was populated with choices when I first stood the server up. Testing bears this out, because if I restart Django, the new choice appears on the list.
How can I get around this so that I can create objects and have them appear on this form too?
(More info: the selected thing is submitted with the form in order for processing to be done upon it.)
Thanks...
If I first add a new Thing object, then go to the page with the selector form, I find that the object does not appear in the dropdown. This is presumably the form was populated with choices when I first stood the server up.
Correct, the variable thing_choices is calculated when your code is first run, and if its at the same scope as your form its unlikely to ever run again.
An easier way is to use a ModelChoiceField, which references a model, rather than a ChoiceField. Like so:
class ThingSelector(forms.Form):
thing = forms.ModelChoiceField(queryset=Things.objects.all()
This should mean that as new Thing objects are added, they are able to be selected in the form.

Ember Data Binding

Supposed I have a model containing 2 pre-populated fields. I would like to display these fields in the DOM for the user to modify if necessary. If I bind the DOM to the model using: {{input value=field1}} and {{input value=field2}}, then every time the user types in a character in one of these fields, Ember updates the bound model immediately. This is not the behavior I want. I prefer to show a button; when pressed, I want to validate data in the two fields in relation to each other, and if valid, update the model.
I considered creating mirrored fields in the controller and binding these to the DOM. Then create an action associated with the button to do the validation, and if data is found to be valid, copy data from the controller fields to the respective model fields. This technique may work, but seems like a very round-about way of doing something conceptually simple.
Do one of you Ember gurus out there have an opinion on how best to do this? I'm looking for best practices; please help.
I am not a guru nor am I familiar with client side validation in EmberData since our app uses server side validation.
Are you sure you can't allow your model to be updated immediately (i.e. bound to the template fields)?
If you did bind the model, then you could validate upon submission. If validation fails, you can just rollback the changes. If it passes, you can save the record. This is probably what I would do.
However, you clearly state that you do not want your model updated immediately, which means you don't want to bind your model.
Also, you have to do some processing that depends on multiple fields to validate, so it really sounds like binding to the controller and having a validation action is a good solution and that's what I would do if I wasn't binding to the model.

How do I display a route parameter?

I'm trying to understand Ember. I can't quite work it out. I currently have the routes:
/participants
/list
/search/:query
And I'm trying to figure out how to get my search page to display that query parameter. The navigation works, I just can't figure out how to display that "query" bit (ie, "You searched for Bennie")
(One thing I tried is that I have an App.ParticipantsSearchController with a 'searchtext' in it, and I know I can display that with {{searchtext}} in the template, but even if I try to later do an App.ParticipantsSearchController.searchtext = 'abc', that doesn't get updated. So that doesn't seem to do me any good.)
I would really appreciate the help from somebody who understands Ember better than I do.
Part of the problem is the parameter is supposed to be representing a unique id of a model representing that resource. If you are going in and randomly changing the unique id, technically your model should be completely different. If your model is completely different you should be transitioning to the route using transitionTo from the route(or transitionToRoute from controller) to switch out models.
And intuitivepixel is correct, you need to be using the set on your model, model.set('searchtext', 'mooooooo');
What will really help you will be the query params that Alex Speller is implementing for Ember. See http://discuss.emberjs.com/t/query-string-support-in-ember-router/1962/44. Until then you'll be doing some really weird transitioning in order to get the url to represent the model. Maybe the url shouldn't update until they've pressed search, or something along those lines. And then in that case, you could call transitionTo/transitionToRoute and then the url would be able to match the current model, so you could type in the url and get straight to that search result. BTW, you can add multiple slugs to a single resource if you are going to have multiple search things. If you do that you'll need to add the serialize method to specify each property...
/search/:age/:sex/:country
serialize: function(model) {
// this will make the URL `/search/12/M/Brazil`
return { age: model.age, sex: model.sex, country: model.country };
}

Force loading a single attribute with ember-data

I'm trying to find a way to update a single attribute of a model without it getting dirty. Just like Store.load but with just a single attribute.
The use case is that I want to upload an avatar image (which cannot be done with the current adapter's semantics AFAIK). I do it against a particular API endpoint and I afterwards I want to update the user with the resulting URL (so all his avatar appearances get updated).
I am in the context of a profile edition, so if I just load the whole user I loose all the changes of the form (because the values get overriden).
Is there any way to do so? Thanks!
You could model 'Avatar' separately, as a model in itself instead of an attribute. Then, that model can be updated via the store without worrying about conflicting changes with the rest of the User model.
I can think of two ways, but I haven't tried them. (They're both just workarounds)
You can set a user's property that is not a DS.attr. If it's a normal property, it shouldn't dirty the record.
If url is a database attribute, then create a computed property that listens to URL and use that in your templates.
App.User = Em.extend({
url: DS.attr('string'),
URL: function() {
return this.get('url');
}.property('url')
});
And then user.set('URL', imageUrl)
Another way could be to transition the record to the clean state.
user.get('stateManager').send('becameClean');
The second method is an ugly hack though.