I yesterday asked this question without success but I have still been working on the problem. Save user input which is a string as object in db
. In short the question yesterday described how I wanted to have a registration key in my registration form to be able to restrict who can register themselves.
What I have done now is in the forms.py file have I created a key field as a ModelChoiceField with a textinput as the widget. When I write a key which exists in the DB I get the following error: "Select a valid choice. That choice is not one of the available choices.". I can't use Select as the widget as that would display the keys for the users.
I tried both methods in this post but without any success.
So the question know is, how can I have a TextInput with a ModelChoiceField?
Get the string from the user through a form with a CharField. Assuming you are using the CBV FormView, do the work in the form_valid method. Something like:
def form_valid( self, form):
key = form.cleaned_data['key']
try:
keyobj = Keything.objects.get( key=key)
# it's valid. do whatever.
return super().form_valid( form)
except Keything.DoesNotExist:
form.add_error( 'key', 'This is not a valid key')
return super().form_invalid( form)
Alternatively you could have a Validator on the form's key field which raises ValidationError if the Keything object does not exist.
Related
I have standard Django models with ForeignKey.
Django docs:
"ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet."
and
"If the model field has choices set, then the form field’s widget will be set to Select, with choices coming from the model field’s choices."
Now I have dropdown menu with choices.
I don't want dropdown menu where user can see options. I want CharField(textfield or similar) where user type, but still
that must be one of the options from the database for that field. He must type a valid entry.
I tried:
class TransakcijeForm(forms.ModelForm):
model = models.Transakcije
fields = .....
labels = .....
widgets ={'subscriber':forms.TextInput()}
but I receive the message:
"Select a valid choice. That choice is not one of the available choices."
(entry is correct and it works with dropdown menu)
This is my first question here and I'm sorry if I miss the form.
The reason you are getting that error is because your form is still treating the subscriber field as a ModelChoiceField because you are only overriding what widget is rendered to html. You need to change the actual field type of your field. You can define your form like this:
from django.core.exceptions import ValidationError
class TransakcijeForm(forms.ModelForm):
subscriber = forms.CharField()
class Meta:
model = models.Transakcije
fields = ....
labels = ....
def clean_subscriber(self):
subscriber_id = self.cleaned_data['subscriber']
try:
# adjust this line to appropriately get the model object that you need
subscriber = SubscriberModel.objects.get(id=subscriber_id)
return subscriber
except:
raise ValidationError('Subscriber does not exist')
The line subscriber = forms.CharField() will change the form to treat the field as a CharField rather than a ModelChoiceField. Doing this will cause the form to return the subscriber field value as a string, so you will need to get the appropriate model object based on the value of the field. That is what the clean_subscriber(self) function is for. It needs to be named like clean_<field name>(). That function will take the string that is returned by the form, try and find the correct model object and return it if an object is found. If it finds no matching objects it will raise a ValidationError so the form doesn't submit with a bad value.
What is the best procedure in Django for saving a form related to another model without the use of inline formsets?
Problem setup:
Model Address is related by a foreign key to Model User
Each User can have multiple Addresses. I want to add a new address to an User.
views.py
In the AddAddress class (extending CreateView) the form.errors has the error
{'user': ['This field is required.']}
The user pk is in the url /address/add/<int:pk>
First, as Daniel Roseman noted, have to make sure the field "user" does not exist in the fields list of the form. This will make sure that the form is valid.
Override the form_valid method in the view class to save the form without commiting, then setting the required user to the resulting instance and then invoking the save directly on the it.
def form_valid(self, form):
address_obj = form.save(False)
address_obj.user = User.objects.get(pk=self.kwargs['pk'])
return HttpResponseRedirect(self.get_success_url())
I am creating an expense submission system, which will be multi-user.
For the purpose of this question, there are two models: Claim and Journey. A user creates a claim and each claim can have multiple journeys. I have made a gist of the code snippet as it's quite long.
In this snippet, I have sucessfully:
Made ClaimListView.get_queryset filter by current user, so whoever's logged in can only see a list of their own claims.
Made ClaimCreateView.form_valid set the correct user when the form is submitted.
Made ClaimDetailView.get_queryset filter by current user. If someone tries the url for another user's claim detail, they get a 404 (perfect!)
Done the same as above for JourneyListView
Done the same as above for JourneyDetailView - again 404 if not authroised :D
However, when I access JourneyCreateView via the URL, the dropdown box for claim still shows claims for the other users.
How should I filter the user within the JourneyCreateView class, so that the claim field only shows claims assigned to the current user?
The closest to a solution I've got is this answer which suggests overriding the __init__ function in the JourneyForm which would leave me with this:
class JourneyForm(forms.ModelForm):
class Meta:
model = Journey
fields = ['date', 'distance','claim']
def __init__(self,alloweduser,*args,**kwargs):
super (JourneyForm,self ).__init__(self,*args,**kwargs) # populates the post
self.fields['claim'].queryset = Claim.objects.filter(tech_id=alloweduser)
However I'm not sure how to pass the alloweduser in from JourneyCreateView or, more to the point, obtain the current user in this class.
form_valid isn't any use in this case, as I'm trying to obtain the user prior to the form being submitted.
In views, the request the view is handling is stored in self.request, so you can obtain the user with self.request.user, and its id with self.request.user.id.
A Django view with the FormMixin [Django-doc] has a method that can be overwritten to pass parameters: get_form_kwargs() [Django-doc].
So we can implement this as:
from django.views.generic.edit import CreateView
class JourneyCreateView(CreateView):
model = Journey
form_class = JourneyForm
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs(*args, **kwargs)
kwargs['alloweduser'] = self.request.user.id
return kwargs
# ...
I have created a form to update the existing user profile. But when i save the form it shows the error user already exists.
I used another approach by getting the user profile and then updating each field, but in that case each field has to be validated?
Any clue how to save the form as an update not as a new entry?
I suggest using UpdateView, one of Django's class-based-views for generic editing:
class django.views.generic.edit.UpdateView
A view that displays a
form for editing an existing object, redisplaying the form with
validation errors (if there are any) and saving changes to the object.
This uses a form automatically generated from the object’s model class
(unless a form class is manually specified).
I managed to get the answer, i imported the form to
import the user that i want to edit
u = User.objects.get(username = user_name)
creating the form with values from existing in database and updating with values from POST
user_form = UserEditForm(request.POST,instance=u)
save the form, since it already has existing record it will update
user_form.save()
I'm trying to do this in Django:
When saving an object in the Admin I want to save also another object of a different type based on one of the fields in my fist object.
In order to do this I must check if that second object already exists and return an validation error only for the particular field in the first object if it does.
My problem is that I want the validation error to appear in the field only if the operation is insert.
How do I display a validation error for a particular admin form field based on knowing if the operation is update or insert?
P.S. I know that for a model validation this is impossible since the validator only takes the value parameter, but I think it should be possible for form validation.
This ca be done by writing a clean_[name_of_field] method in a Django Admin Form. The insert or update operation can be checked by testing self.instance.pk.
class EntityAdminForm(forms.ModelForm):
def clean_field(self):
field = self.cleaned_data['field']
insert = self.instance.pk == None
if insert:
raise forms.ValidationError('Some error message!')
else:
pass
return field
class EntityAdmin(admin.ModelAdmin):
form = EntityAdminForm
You have to use then the EntityAdmin class when registering the Entity model with the Django admin:
admin.site.register(Entity, EntityAdmin)
You can write your custom validation at the model level:
#inside your class model ...
def clean(self):
is_insert = self.pk is None
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
#do your business rules
if is_insert:
...
if __some_condition__ :
raise ValidationError('Dups.')
Create a model form for your model. In the clean method, you can set errors for specific fields.
See the docs for cleaning and validating fields that depend on each other for more information.
That is (probably) not an exact answer, but i guess it might help.
Django Admin offers you to override save method with ModelAdmin.save_model method (doc is here)
Also Django api have a get_or_create method (Doc is here). It returns two values, first is the object and second one is a boolean value that represents whether object is created or not (updated an existing record).
Let me say you have FirstObject and SecondObject
In your related admin.py file:
class FirstObjectAdmin(admin.ModelAdmin):
...
...
def save_model(self, request, obj, form, change):
s_obj, s_created = SecondObject.objects.get_or_create(..., defaults={...})
if not s_created:
# second object already exists... We will raise validation error for our first object
...
For the rest, I do not have a clear idea about how to handle it. Since you have the form object at hand, you can call form.fields{'somefield'].validate(value) and write a custom validation for admin. You will probably override clean method and try to trigger a raise ValidationError from ModelAdmin.save_model method. you can call validate and pass a value from there...
You may dig django source to see how django handles this, and try to define some custom validaton steps.