I'm creating my first site using Django and having trouble loading initial data into a ModelForm linked to the built in Django group table.
Right now, users can go to a group page and select from an array of groups they would like to join. When they return to the page later, they see the same list of options/checkboxes again with no indication of which groups they already belong to.
I can't figure out how to have the intial group data load into the form, such that if you are already a member of "group 1" for example, that checkbox is already checked. I would also like to have it so that you could uncheck a box, and so when you submit the form you could be leaving some groups and joining others at the same time. Any help appreciated! My code below:
class GroupForm(ModelForm):
groupOptions = forms.ModelMultipleChoiceField(queryset=Group.objects.all(), label = "Choose your groups",
widget=forms.CheckboxSelectMultiple())
class Meta:
model = Group
fields = ['groupOptions']
def groupSelect(request):
if request.method == 'POST':
form = GroupForm (request.POST)
if form.is_valid():
group = form.cleaned_data['groupOptions']
request.user.groups = group
return render (request, 'groups/groupSelect.html' , {'form':form})
else:
form = GroupForm()
return render (request, 'groups/groupSelect.html' , {'form':form})
Took me a few days and some trial and error, but figured this one out on my own. Just needed to modify the second to last line in the code above. The ModelForm loads all available groups as options, and the line below causes the groups the user already belongs to to be checked.
form = GroupForm(initial={ 'groupOptions': request.user.groups.all() })
Related
Trying to paginate a large queryset so I can return to the same position I was in previously even if data has been added to the database.
Currently I have as my pagination class:
from rest_framework.pagination import CursorPagination
class MessageCursorPagination(CursorPagination):
page_size = 25
ordering = '-date'
In my View I have:
from rest_framework.generics import GenericAPIView
from rest_framework.authentication import TokenAuthentication, BasicAuthentication
class MessageViewSet(GenericAPIView):
permission_classes = (IsAuthenticated, )
authentication_classes = (TokenAuthentication,)
pagination_class = pagination.MessageCursorPagination
serializer_class = serializers.MessageSerializer
def get(self, request, **kwargs):
account_id = kwargs.get('account_id', None)
messages = models.Message.objects.filter(
account=account_id)
paginated_messages = self.paginate_queryset(messages)
results = self.serializer_class(paginated_messages, many=True).data
response = self.get_paginated_response(results)
return response
While testing to see if I'd set it up right, I got the results I was expecting with a next link and a null for the previous link.
After going to the next link I get a new next link, the next set of results, and a previous link.
When I continue to the next link I get the same previous link as before, but with the next, next link and the next set of data.
No matter how many times I go to the next, next link the previous link remains the same.
Why doesn't the previous link update?
-- Update --
It looks like the cause to my issue is that I have a lot of messages on the same date. Ordering by date it tries to step back to the date before the current cursor. How can I order by date but step through the list using the cursor pagination like I would with ids?
From the Documentation
Proper usage of cursor pagination should have an ordering field that satisfies the following:
Should be an unchanging value, such as a timestamp, slug, or other field that is only set once, on creation.
Should be unique, or nearly unique. Millisecond precision timestamps are a good example. This implementation of cursor pagination uses a smart "position plus offset" style that allows it to properly support not-strictly-unique values as the ordering.
Should be a non-nullable value that can be coerced to a string.
I'm wondering where should I put a validation form which accessing database.
Basically I will need user to enter item_type and I want to check first i the item_type has exist in database.
There are 3 options:
In the database model, I have ItemType class and I put function add() which will check if existing item has exist or not
In view, so in the route of the page, from wtforms form.validate_on_submit(), I do a check to get data from database, and if exist I will put error in here
In wtforms validate(), adding extra validation after default validation of the Form class
I've seen people using number 2 and 3, but not sure which one is the best. The error message that I want will also need to display it on the specific field of the form (this is achieveable by method 2 and 3 since they have reference to the form field) but then again since it is related to accessing database, maybe it is better to put everything regarding database access to model functions?
In my opinion, if it comes from a form then it should be validated on that form and then raise an error for that specific field when invalid. See the example bellow:
class SigninForm(Form):
"""Form for signin"""
email = StringField('Email',
validators=[
DataRequired("Email shouldn't be empty."),
Email('Email format is not correct.')
])
password = PasswordField('Password',
validators=[DataRequired("Password shouldn't be empty.")])
def validate_email(self, field):
"""
verify if account exists and if not raise an error in
that field.
"""
user = User.query.filter(User.email == self.email.data).first()
if not user:
raise ValueError("Account doesn't exist.")
def validate_password(self, field):
"""
Verify if password is valid and if not raise an error in
that field.
"""
if self.email.data:
user = User.query.filter(User.email == self.email.data).first()
if not user or not user.check_password(self.password.data):
raise ValueError('Password is not correct.')
else:
self.user = user
The view function for this example:
#app.route('/signin', methods=['GET', 'POST'])
def signin():
"""Signin"""
form = SigninForm()
if form.validate_on_submit():
# sign in function to register user into a session
signin_user(form.user)
return redirect(url_for('site.index'))
return render_template('account/signin/signin.html', form=form)
I'm getting a strange result whereby POSTing JSON to a DRF endpoint returns:
{"photos":["This field is required."],"tags":["This field is required."]}'
Whereas when POSTing form data DRF doesn't mind that the fields are empty.
My model is:
class Story(CommonInfo):
user = models.ForeignKey(User)
text = models.TextField(max_length=5000,blank=True)
feature = models.ForeignKey("Feature", blank=True, null=True)
tags = models.ManyToManyField("Tag")
My serializer is:
class StorySerializer(serializers.HyperlinkedModelSerializer):
user = serializers.CharField(read_only=True)
def get_fields(self, *args, **kwargs):
user = self.context['request'].user
fields = super(StorySerializer, self).get_fields(*args, **kwargs)
fields['feature'].queryset = fields['feature'].queryset.filter(user=user)
fields['photos'].child_relation.queryset = fields['photos'].child_relation.queryset.filter(user=user)
return fields
class Meta:
model = Story
fields = ('url', 'user', 'text', 'photos', 'feature', 'tags')
And my api.py is:
class StoryViewSet(viewsets.ModelViewSet):
serializer_class = StorySerializer
def get_queryset(self):
return self.request.user.story_set.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
The results:
# JSON request doesn't work
IN: requests.post("http://localhost:8001/api/stories/",
auth=("user", "password",),
data=json.dumps({'text': 'NEW ONE!'}),
headers={'Content-type': 'application/json'}
).content
OUT: '{"photos":["This field is required."],"tags":["This field is required."]}'
# Form data request does work
IN: requests.post("http://localhost:8001/api/stories/",
auth=("user", "password",),
data={'text': 'NEW ONE!'},
).content
OUT: '{"url":"http://localhost:8001/api/stories/277/","user":"user","text":"NEW ONE!","photos":[],"feature":null,"tags":[]}'
The issue here isn't obvious at first, but it has to do with a shortcoming in form-data and how partial data is handled.
Form data has two special cases that Django REST framework has to handle
There is no concept of "null" or "empty" data for some inputs, including checkboxes and other inputs that allow for multiple selections.
There is no input type that supports multiple values for a single field, checkboxes being the one exception.
Both of these combine together to make it difficult to handle accepting form data within Django REST framework, so it has to handle a few things differently from most parsers.
If a field is not passed in, it is assumed to be None or the default value for the field. This is because inputs with no values are not passed along in the form data, so their key is missing.
If a single value is passed in for a multiple-value field, it will be treated like the one selected value. This is because there is no difference between a single checkbox selected out of many and a single checkbox at all in form data. Both of them are passed in as a single key.
But the same doesn't apply to JSON. Because you are not passing an empty list in for the photos and tags keys, DRF does not know what to give it for a default value and does not pass it along to the serializer. Because of this, the serializer sees that there is nothing passed in and triggers the validation error because the required field was not provided.
So the solution is to always provide all keys when using JSON (not including PATCH requests, which can be partial), even if they contain no data.
I have a form that has some input text boxes and well as some select boxes. I have validation working perfectly on the textboxes. I don't care if any of the select boxes are left defualt or not, but everytime I submit the form it goes to a pylons error page saying "Invalid: Please enter value for ..." But I don't want that to happen.
here is my validator function:
class Registration(formencode.Schema):
allow_extra_fields=True
first_name = formencode.validators.String(strip=True, not_empty=True)
last_name = formencode.validators.String(strip=True, not_empty=True)
address = formencode.validators.String(strip=True, not_empty=True)
phone = formencode.validators.PhoneNumber(strip=True, not_empty=True)
email = formencode.validators.Email(resolve_domain=True, not_empty=True)
messages = {
'tooLow': "You must be at least 18 in order to register for an event.",
'tooHigh': "You obviously are not that old. Please enter your actual age.",
}
age = formencode.validators.Int(min=18, max=999, not_empty=True, messages=messages)
I thought with allow_extra_fields = True it would allow for fields in the form that aren't supplied in the function to pass if left blank/default. I have select boxes named heard, frequency, and level that should be ignored.
Any help here would be greatly appreciated.
On your formencode.validators methods:
Use not_empty=False if your form is submitting the fields and the values are empty strings. The param not_empty false is how you say: empty is a valid value.
If the fields do not exist then use if_missing = None which will set your result to None.
I think you should add the following line to it
filter_extra_fields = True
My main question here is dealing with the pramas map when having a one-to-many relationship managed within one dynamic form, as well as best practices for dealing with one-to-many when editing/updating a domain object through the dynamic form. The inputs for my questions are as follows.
I have managed to hack away a form that allows me to create the domain objects shown below in one Dynamic form, since there is no point in having a separate form for creating phone numbers and then assigning them to a contact, it makes sense to just create everything in one form in my application. I managed to implement something similar to what I have asked in my Previous Question (thanks for the people who helped out)
class Contact{
String firstName
String lastName
// ....
// some other properties
// ...
static hasMany = [phones:Phone]
static mapping = {
phones sort:"index", cascade: "all-delete-orphan"
}
}
class Phone{
int index
String number
String type
Contact contact
static belongsTo = [contact:Contact]
}
I basically managed to get the values from the 'params' map and parse them on my own and create the domain object and association manually. I.e. i did not use the same logic that is used in the default scaffolding, i.e.
Contact c = new Contact(params)
etc...., i just looped through all the params and hand crafted my domain objects and saved them and everything works out fine.
My controller has code blocks that look like this (this is stripped down, just to show a point)
//create the contact by handpicking params values
def cntct = new Contact()
cntct.firstName = params.firstName
cntct.lastName = params.lastName
//etc...
//get array of values for number,type
def numbers = params['phone.number']
def types = params['phone.type']
//loop through one of the arrays and create the phones
numbers.eachWithIndex(){ num, i ->
//create the phone domain object from
def phone = new Phone()
phone.number = num
phone.type = types[i]
phone.index = i
cntct.addToPhones(phone)
}
//save
My questions are as follows:
What is the best practice of handeling such a situation, would using Command objects work in this case, if yes where can i found more info about this, all the examples I have found during my search deal with one-to-one relationships, I couldn't find an example for one-to-many?
What is the best way to deal with the relatiohsips of the phones in this case, in terms of add/removing phones when editing the contact object. I mean the creation logic is simple since I have to always create new phones on save, but when dealing with updating a contact, the user might have removed a phone and/or editing an exiting one and/or added some new phones. Right now what I do is just delete all the phones a contact has and re-create them according to what was posted by the form, but I feel that's not the best way to do it, I also don't think looping over the existing ones and comparing with the posted values and doing a manual diff is the best way to do it either, is there a best practice on how to deal with this?
Thanks, hopefully the questions are clear.
[edit] Just for more information, phone information can be added and deleted dynamically using javascript (jquery) within the form [/edit]
disclaimer: i do not know if the following approach works when using grails. Let me know later.
See better way for dynamic forms. The author says:
To add LineItems I have some js that calculates the new index and adds that to the DOM. When deleting a LineItem i have to renumber all the indexes and it is what i would like to avoid
So what i do
I have a variable which stores the next index
var nextIndex = 0;
When the page is loaded, i perform a JavaScript function which calculates how many child The collection has and configure nextIndex variable. You can use JQuery or YUI, feel free.
Adding a child statically
I create a variable which store the template (Notice {index})
var child = "<div>"
+= "<div>"
+= "<label>Name</label>"
+= "<input type="text" name=\"childList[{index}].name\"/>"
+= "</div>"
+= "</div>"
When the user click on the Add child button, i replace {index} - by using regex - by the value stored in the nextIndex variable and increment by one. Then i add to the DOM
See also Add and Remove HTML elements dynamically with Javascript
Adding a child dinamically
Here you can see The Paolo Bergantino solution
By removing
But i think it is the issue grow up when deleting. No matter how many child you remove, does not touch on the nextIndex variable. See here
/**
* var nextIndex = 3;
*/
<input type="text" name="childList[0].name"/>
<input type="text" name="childList[1].name"/> // It will be removed
<input type="text" name="childList[2].name"/>
Suppose i remove childList1 What i do ??? Should i renumber all the indexes ???
On the server side i use AutoPopulatingList. Because childList1 has been removed, AutoPopulatingList handles it as null. So on the initialization i do
List<Child> childList = new AutoPopulatingList(new ElementFactory() {
public Object createElement(int index) throws ElementInstantiationException {
/**
* remove any null value added
*/
childList.removeAll(Collections.singletonList(null));
return new Child();
}
});
This way, my collection just contains two child (without any null value) and i do not need to renumber all the indexes on the client side
About adding/removing you can see this link where i show a scenario wich can gives you some insight.
See also Grails UI plugin
Thanks,
Your answer brought some insight for me to do a wider search and I actually found a great post that covers all the inputs in my question. This is just a reference for anyone reading this. I will write a blog entry on how I implemented my case soon, but this link should provide a good source of ino with a working exmaple.
http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/
Most of the time I use ajax to manage such problem.
So when the user clicks add new phone I get the template UI from the server for manageability purpose ( the UI just same GSP template that I use to edit, update the phone), so this way you are not mixing your UI with your js code, whenever you want to change the UI you have to deal only with our GSP code.
Then after getting the UI I add it to the page using jquery DOM manipulation. Then after filling the form when they hit add(save) the request is sent to the server via ajax and is persisted immediately.
When the user clicks edit phone the same UI template is loaded from the server filled with existing phone data, then clicking update will update the corresponding phone immediately via ajax, and same thing applies to delete operation.
But one day I got an additional scenario for the use case that says, "until I say save contact no phone shall be saved on the backend, also after adding phones to the contact on the ui if navigate away to another page and come back later to the contact page the phones I added before must be still there." ugh..
To do this I started using the Session, so the above operations I explained will act on the phone list object I stored on the session instead of the DB. This is simple perform all the operation on the phonesInSession but finally dont forget to do this(delete update):
phonesToBeDeleted = phonesInDB - phonesInSession
phonesToBeDeleted.each{
contact.removeFromPhones(it)
it.delete()
}
I know I dont have to put a lot of data in session but this is the only solution I got for my scenario.
If someone has got similar problem/solution please leave a comment.
First, in all your input fields names you add an #:
<input type="text" name="references[#].name"/>
Second, add call a function before submitting:
<g:form action="save" onsubmit="replaceAllWildCardsWithConsecutiveNumbers();">
Third, this is the code for the function that you call before submitting the form:
function replaceAllWildCardsWithConsecutiveNumbers(){
var inputs = $('form').find("[name*='#']");
var names = $.map(inputs, function(el) { return el.name });
var uniqueNames = unique(names);
for (index in uniqueNames) {
var uniqueName = uniqueNames[index];
replaceWildCardsWithConsecutiveNumbers("input", uniqueName);
replaceWildCardsWithConsecutiveNumbers("select", uniqueName);
}
}
function unique(array){
return array.filter(function(el, index, arr) {
return index === arr.indexOf(el);
});
}
function replaceWildCardsWithConsecutiveNumbers(inputName, name){
counter = 0;
$(inputName + "[name='" + name + "']").each(function (i, el) {
var curName = $(this).attr('name');
var newName = curName.replace("#", counter);
$(this).attr('name', newName);
counter += 1;
});
}
Basically, what the code for replaceAllWildCardsWithConsecutiveNumbers() does, is to create a list for all input (or select) elements whose name contains an #. Removes the duplicates. And then iterates over them replacing the # with a number.
This works great if you have a table and you are submitting the values to a command object's list when creating a domain class for the first time. If you are updating I guess you'll have to change the value of counter to something higher.
I hope this helps someone else since I was stuck on this issue for a while myself.