Nathan Welsh
Nathan Welsh

Reputation: 17

Setting the field value of a form in view.py

My django website would like to allow logged in users to post a recipe. The recipe model is linked to the user model and as such requires a valid user instance. I would like the Recipe form to automatically assign the postedby field to the instance of the logged in user.

So far I have attempted to pass in a dictionary, storing the user's name, to the form instance. as shown in the view However, the constructor method is not receiving the data and is rendering the form but with a failed attempt to submit.

I cannot store the data without postedby field having a valid instance as the model throws the following error:

Exception Value:UserProfile matching query does not exist.

I have also tried to do the following in views.py;

@login_required
def add_recipe(request): 
    form = RecipeForm()
    form.fields['postedby'] = UserProfile.objects.get(user=User.objects.get(username=request.user.__str__()))
    context_dict = {'form':form}
    if request.method == 'POST': 
        ...

However this overwrites the postedby form view to be rendered and raises and error.

views.py

@login_required
def add_recipe(request): 
    form = RecipeForm({'user':request.user})
    context_dict = {}


    #print(form.fields['postedby'].queryset)

    if request.method == 'POST': 
        form = RecipeForm(request.POST)        
        if form.is_valid(): 
            form.save(commit=True)
            return redirect(reverse('spatula:index'))
        else: 
            print(form.errors)
    context_dict['form'] = form

    return render(request, 'spatula/add_recipe.html', context=context_dict)

The RecipeForm is as follows:

class RecipeForm(forms.ModelForm):

    def __init__(self,*args,**kwargs):
        print(kwargs) 
        super(RecipeForm, self).__init__(*args, **kwargs)

    #input fields for recipe form
    method = forms.CharField(max_length=512, widget=forms.Textarea(attrs={'placeholder':'Method'}))
    name = forms.CharField(max_length=128, widget=forms.TextInput(attrs={'placeholder':'Recipe Name'}))
    ingredients = forms.CharField(max_length=512, widget=forms.Textarea(attrs={'placeholder':'Ingredients'}))

    category = NameChoiceField(widget=forms.Select(), queryset =Category.objects.all(), initial = 0)

    toolsreq = forms.CharField(max_length=512, widget=forms.TextInput(attrs={'placeholder':'Tools Required'}))
    difficulty = forms.IntegerField(widget=forms.NumberInput(attrs={'type':'range', 'step':'1', 'min':'1','max':'3'}), help_text = 'Difficulty: ')
    cost = forms.IntegerField(widget=forms.NumberInput(attrs={'type':'range', 'step':'1', 'min':'1','max':'3'}), help_text = 'Cost: ')
    diettype = forms.IntegerField(widget=forms.RadioSelect(choices=DIET_CHOICES))

    # not required as its not stored in DB
    #description = forms.CharField(widget=forms.Textarea(attrs={'placeholder':'Description'})) 


    #hidden fields
    rating = forms.FloatField(widget=forms.HiddenInput(), initial=0, required=False)
    slug = forms.SlugField(widget=forms.HiddenInput(),required=False)
    postedby = forms.SlugField(widget=forms.HiddenInput(),required=False)

    #Order in which inputs get rendered
    field_order = ['name', 'category', 'toolsreq', 'difficulty', 'cost', 'diettype', 'ingredients', 'method']

    class Meta: 
        model = Recipe
        exclude = ('id',)

finally here is the recipe model:

class Recipe(models.Model):

    DIET_CHOICES = (
                (1,"Meat"),
                (2,"Vegetarian"),
                (3,"Vegan"),
    )
    DIFFICULTY_CHOICES   = (
                (1,1),
                (2,2),
                (3,3),
    )
    COST_CHOICES        = (
                (1,1),
                (2,2),
                (3,3),
    )

    name                   = models.CharField(max_length=NAME_MAX_LENGTH)
    ingredients            = models.TextField(max_length=MAX_TEXT_LENGTH)
    toolsreq               = models.TextField(max_length=MAX_TEXT_LENGTH)
    method                 = models.TextField()

    # make sure the form views for difficulty, 
    # cost and diettype datatypes restrict the 
    # users selection to the CHOICES above
    difficulty             = models.PositiveSmallIntegerField(choices=DIFFICULTY_CHOICES)
    cost                   = models.PositiveSmallIntegerField(choices=COST_CHOICES)
    diettype               = models.PositiveSmallIntegerField(choices=DIET_CHOICES)

    postedby               = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=0)

    #  - Following fields are hidden when creating a new recipe
    #       ratings are out of 5, to 1 decimal place.
    #  - Need a script to update rating everytime
    #       a new rating for the recipe is posted.
    rating                 = models.DecimalField(decimal_places=1,max_digits=3, default=0)
    category               = models.ForeignKey(Category,to_field="name", on_delete=models.CASCADE)

    # recipes rating is calculated when the recipe is requested, no value to be stored

    def __str__(self):
        return self.name

    # used for recipe mappings
    def save(self,*args, **kwargs):
        self.slug = slugify(str(self.name)+str(self.postedby))
        super(Recipe,self).save(*args, **kwargs)

Upvotes: 0

Views: 1542

Answers (2)

kyore
kyore

Reputation: 842

The error says: Exception Value:UserProfile matching query does not exist. It means there is no UserProfile object for that.

You probably want:

try:
    profile = UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist
    # your logic

Or if you want to automatically create UserProfile object for requested user. You can use get_or_create:

p, created = UserProfile.objects.get_or_create(user=request.user)

Explanation: Any keyword arguments passed to get_or_create() — except an optional one called defaults — will be used in a get() call. If an object is found, get_or_create() returns a tuple of that object and False.

Upvotes: 0

dirkgroten
dirkgroten

Reputation: 20692

Since you don't want your user go edit this field, remove it entirely from the form:

exclude = ['id', 'postedby']

Then in your view, set the value on the instance before saving:

# ...
if request.method == 'POST': 
    form = RecipeForm(request.POST)        
    if form.is_valid(): 
        recipe = form.save(commit=False)
        recipe.postedby = UserProfile.objects.get(user=request.user)
        recipe.save()
        return redirect(reverse('spatula:index'))
# ...

Upvotes: 2

Related Questions