9

I'm trying to create my first own project in python django. What I'v done so far is a simple shop. When I click this:

item_details.html

<a href="{% url 'buy_item' pk=item.pk %}" class="submit">Kup teraz</a>

I will be redirected

urls.py

path('buy/<int:pk>', ItemCreate.as_view(), name='buy_item')

To this form:

form.py

class BuyForm(forms.ModelForm):

    class Meta:
        model = Order
        fields = (
        'item',
        'delivery',
        'price',
        'buyer',)

With this view:

views.py

class ItemCreate(CreateView):
    model = Order
    fields = ['item', 'price', 'buyer', 'delivery']

    def get_initial(self):
        item = get_object_or_404(Item, pk=self.kwargs['pk'])
        self.initial.update({
            'buyer': self.request.user,
            'price': item.price,
            'item': item.pk,
        })
        return super(ItemCreate, self).get_initial()

models.py

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

class Category(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.title
class Item(models.Model):
    seller = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    price = models.FloatField(null=False, blank=False)
    img = models.ImageField(blank=True, null=True,
        upload_to='covers/%Y/%m/%D/')
    published_date = models.DateTimeField(default=timezone.now)
    def __str__(self):
        return self.title

class Supplier(models.Model):
    title = models.CharField(max_length=100)
    def __str__(self):
        return self.title

class Delivery(models.Model):
    title = models.ForeignKey(Supplier, on_delete=models.CASCADE)
    price_of_delivery = models.FloatField(null=False, blank=False)
    def __str__(self):
        return self.title

class Order(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    delivery = models.ForeignKey(Delivery, on_delete=models.CASCADE)
    order_date = models.DateTimeField(default=timezone.now)
    buyer = models.ForeignKey(User, on_delete=models.CASCADE)
    price = models.FloatField(null=False, blank=False)
    def __str__(self):
        return self.item

Because the initial data shouldn't be edited by user, I'd like to make them readonly.

What I've tried so far is:

class BuyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
       super(BuyForm, self).__init__(*args, **kwargs)
       self.fields['buyer'].widget.attrs['readonly'] = True    

    class Meta:
        model = Order
        fields = (
        'item',
        'delivery',
        'price',
        'buyer',)

No errors in my console but also there's no results.

My order_form.html looks that:

{% extends 'shop/base.html' %}
{% block content %}

<div align="center">
    <h1>Buy item</h1>
    <form method="POST" class="post-form">{% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="save btn btn-default">Buy</button>
    </form>
</div>
<
{% endblock %}

What should I do to make "buyer", "price" and the "item" readonly?

Eliro
  • 223
  • 2
  • 4
  • 12

4 Answers4

6

All other answers are great and correct, but that didn't solve the problem when I created your example.

Actual problem is that your view never use your form in a first place - that's why overriding attributes did nothing!

More about class-based views with custom forms from django docs

views.py

from django.shortcuts import get_object_or_404
from django.views.generic import CreateView
from .models import Item
from .forms import BuyForm

class ItemCreate(CreateView):
    form_class = BuyForm
    template_name = 'order_form.html'
    success_url = '/thanks/'

    def get_initial(self):
        item = get_object_or_404(Item, pk=self.kwargs['pk'])
        self.initial.update({
            'buyer': self.request.user.id,
            'price': item.price,
            'item': item.pk,
        })
        return super(ItemCreate, self).get_initial()

forms.py

from django import forms
from .models import Order


class BuyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BuyForm, self).__init__(*args, **kwargs)
        self.fields['buyer'].disabled = True

    class Meta:
        model = Order
        fields = (
            'item',
            'delivery',
            'price',
            'buyer',)
Gasanov
  • 2,839
  • 1
  • 9
  • 21
  • 2
    Fields are disabled but it shows "Select a valid choice. That choice is not one of the available choices." and I can not submit my order. – Eliro Apr 18 '19 at 16:56
  • @Eliro You need to provide `user.id` in initials instead of just `user`. I updated my answer. – Gasanov Apr 18 '19 at 17:07
1

try this:

class BuyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
       super(BuyForm, self).__init__(*args, **kwargs)
       self.fields['buyer'].required = False
       self.fields['buyer'].widget.attrs['disabled'] = "disabled" 

       ...

    def clean_buyer(self):
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.buyer
        else:
            return self.cleaned_data.get('buyer', None)

but the filed with disabled attr will post blank data. That why we override the clean method to set the field's value to be what was originally in the instance.

dpht
  • 156
  • 4
1

What you are looking for is a formfield's disabled attribute:
https://docs.djangoproject.com/en/2.2/ref/forms/fields/#disabled

CoffeeBasedLifeform
  • 2,296
  • 12
  • 27
-4

Another approach is to make them hidden fields. Add this to the Meta...

class Meta:
    widgets = {
        'buyer': forms.HiddenInput(),
        'price': forms.HiddenInput(),
        'item': forms.HiddenInput(),
    }
Mark Bailey
  • 1,617
  • 1
  • 7
  • 13
  • No results and no errors. I still can edit initial data. By the way - I'd like to make a READonly fields. How can I READ hidden input? – Eliro Apr 18 '19 at 16:22