-1

I have a form which contains an element that is not bound to a model. There are cases where this form element will not be rendered in the HTML at all, but when I post the form I want it to validate still if it doesn't exist.

I have looked into the possibility of changing the value that is posted to something, but can't see a way of doing this. I have tried overriding the clean method, but I am not sure how you do that. I have tried setting it to required=False, but that has no effect because it seems to require a null value to be posted at the very least.

My form class looks as below:

class StimForm(ModelForm):
    body = forms.CharField( widget=forms.Textarea )
    userstims = forms.ChoiceField(required=False)

    class Meta:
        model = Stim
        fields = ['body','privacytype','stimtype']

The HTML is below. As this is possibly hidden the data for userstim is not posted in some cases. I still want the form validation to work in these cases.

  <div class='form-group row' id="userstim" style="display:none;">
      <label class="col-2 col-form-label">Add Custom Stim:</label>
      <div class="col-5">
        {{ form.userstims }}
      </div>
      <div class="col-5">
        <a href="/stimbook/adduserstim">Add Stim</a>
      </div>
  </div>

UPDATE - The View:

def stimboard(request):
    user = getuser(request)
    if user == None:
        #redirect
        return HttpResponseRedirect('/user/login')
    #Get the user defined stims if they exist
    try:
        userstims = UserStim.objects.filter(user=user)
    except:
        userstims = []
    #Get the id of the user to look up
    stimuser = User.objects.get(id=request.GET.get("id"))
    #Get the user profile data
    profiledata = getprofiledata(stimuser)
    #forms
    commentform = StimCommentForm()
    if request.POST:
        form = StimForm(request.POST,mystims=userstims)
        userstimform = UserStimForm(request.POST)
        if form.is_valid():
            #Create stim
            print("Creating Stim")
            if form.cleaned_data['stimtype'] == "OT":
                #Create custom stim
                Stim.objects.create(
                    body = form.cleaned_data['body'],
                    poster = user,
                    board = stimuser,
                    privacytype = form.cleaned_data['privacytype'],
                    stimtype = form.cleaned_data['stimtype'],
                    otherstim = UserStim.objects.get(id=form.cleaned_data['userstims'])
                )
            else:
                Stim.objects.create(
                    body = form.cleaned_data['body'],
                    poster = user,
                    board = stimuser,
                    privacytype = form.cleaned_data['privacytype'],
                    stimtype = form.cleaned_data['stimtype']
                )
    else:
        form = StimForm(request.POST,mystims=userstims)
        userstimform = UserStimForm()
    #Get friendship status of user
    buddystatus = Buddy.buddies.buddystatus(user,stimuser)
    #Get public stims from user
    stims = Stim.objects.filter(board=stimuser,privacytype="PU")
    #Check if buddy and get private stims then add them to the stims
    isbuddy = Buddy.buddies.isbuddy(user,stimuser)
    if isbuddy:
        privatestims = Stim.objects.filter(board=stimuser,privacytype="PR")
        stims = stims | privatestims
    stimlist = []
    #get the comments for each stim
    for stim in stims:
        stimdict = dict()
        stimdict['id'] = stim.id
        stimdict['poster'] = stim.poster
        stimdict['body'] = stim.body
        stimdict['dateofpost'] = stim.dateofpost
        stimdict['privacytype'] = stim.privacytype
        if stim.stimtype == "OT":
            #get the custom stim
            stimdict['stimtype'] = stim.otherstim.stimname
        else:
            print(type(stim.stimtype))
            stimdict['stimtype'] = getstimtype(stim.stimtype)
        stimdict['stimcomments'] = StimComment.objects.filter(stim=stim)
        stimlist.append(stimdict)
    stimlist.sort(key=lambda x: x['dateofpost'], reverse=True)
    return render(request, 'stimboard/stimboard.html', { 'stimuser' : stimuser, 'stims' : stimlist, 'buddystatus' : buddystatus,
                                                        'commentform' : commentform, 'form' : form, 'userstimform' : userstimform,
                                                         'isbuddy' : isbuddy, 'profiledata' : profiledata })

UPDATE - The init method

def __init__(self, *args, **kwargs):
        mystimsqs = kwargs.pop('mystims')
        super(StimForm, self).__init__(*args, **kwargs)
        print("kwargs")

        mystims = []
        for stim in mystimsqs:
            stimlist = (stim.id,stim.stimname)
            mystims.append(stimlist)
        self.fields['userstims'] = forms.ChoiceField(
            choices=tuple(mystims)
        )
OptimusPrime
  • 777
  • 16
  • 25
  • This question is not clear. If the field is not required, then the form is valid without it. What *exactly* is the problem you are having? – Daniel Roseman Apr 19 '19 at 11:50
  • The HTML shows a div which is set to no display through the style attribute. If it doesn't display then {{ form.userstims }} is not posted at all which means it fails on form validation because it doesn't exist at all. – OptimusPrime Apr 19 '19 at 12:43
  • No, it wouldn't fail; the `userstims` field has required=False. If you're getting an error, show the whole thing, plus the relevant view. – Daniel Roseman Apr 19 '19 at 12:59
  • I have answered below on what works, but it is not an ideal solution. – OptimusPrime Apr 19 '19 at 14:27

1 Answers1

0

In your __init__ method you are completely redefining the userstims field, overwriting it with a new field that does not have required=False set. You should either pass in that option in the redefinition, or better avoid redefining the whole field and just set the choices directly:

self.fields['userstims'].choices = mystims

Note also, your loop would be better written as a list comprehension:

mystims = [(stim.id,stim.stimname) for stim in mystimqs]
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895