Фильтрация данных в реальном времени в формах Django - PullRequest
0 голосов
/ 20 октября 2019

У меня две проблемы строго. Первая - это проблема с типом поля в forms.py. Потому что я пытаюсь использовать внешний ключ в качестве значения в флажке, и у меня появляется ошибка, что «int () должен быть строкой, байтовым объектом или числом, а не« ModelChoiceField », и я неНе знаю, что с этим делать. Вторая главная проблема - фильтрация данных в интерфейсе в реальном времени. Что я имею в виду? У меня есть такая модель пользователя:

# user/models.py:
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
    country= models.ForeignKey(Country, on_delete=models.SET_NULL, null=True)
    city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True)
    year = models.IntegerField(choices=YEARS, default=1)
    image = models.ImageField(default='default.jpg', upload_to='profile_pics')

А в формах я хочу видеть толькоте города, которые находятся в выбранной стране. Например, у нас есть запись типа:

London, United Kingdom;
York, United Kingdom; 
Berlin, Germany;

, и если пользователь выбирает Германию, он должен видеть только Берлин в районе с городами. Я надеюсь, что вы знаете, чтоЯ хочу достичь, и кто-то сможет мне помочь.

# forms.py:

class ProfileUpdateForm(forms.ModelForm):
    country =forms.ModelChoiceField(queryset=Country.objects.all())
    city = forms.ModelChoiceField(queryset=City.objects.filter(country=country))

    class Meta:
        model = Profile
        fields = ['website','country', 'city', 'year', 'image']


# city/models.py

class Country(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class City(models.Model):
    name = models.CharField(max_length=100)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    code = models.CharField(max_length=7)

    def __str__(self):
        return self.name


РЕДАКТИРОВАТЬ: Я думаю, вам нужен этот кусок кода:

@login_required
def profile(request):

    if request.method == 'POST':
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form = ProfileUpdateForm(request.POST,
                                   request.FILES,
                                   instance=request.user.profile)
        if u_form.is_valid() and p_form.is_valid():
            u_form.save()
            p_form.save()
            messages.success(request, 'Twoje dane zostały uaktualnione!')
    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form = ProfileUpdateForm(instance=request.user.profile)

    context = {
        'u_form': u_form,
        'p_form': p_form,
    }

    return render(request, 'users/profile.html', context)

Ответы [ 2 ]

0 голосов
/ 21 октября 2019

Да def load_cities функция должна быть создана в views.py, чуть выше вашей основной функции.

Ваш файл forms.py должен выглядеть следующим образом, и вы можете удалить определения страны и города, поскольку вы уже определили свои поляв классе Meta:

class ProfileUpdateForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ['website','country', 'city', 'year', 'image']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['city'].queryset = City.objects.none()

        if 'country' in self.data:
            try:
                country_id = int(self.data.get('country'))
                self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
            except (ValueError, TypeError):
                pass
        elif self.instance.pk:
            self.fields['city'].queryset = self.instance.country.city_set.order_by('name')

Не могли бы вы показать свою основную функцию, которая отображает вашу форму (в views.py)?

0 голосов
/ 20 октября 2019

Это легко, не волнуйся. Но вы должны использовать базовый Ajax-запрос для этого, чтобы вы узнали что-то новое, будьте счастливы!

Во-первых, когда пользователь еще не выбрал какую-либо страну, мы должны скрыть все города в форме или когда пользовательвыберите неопределенную страну (возможно, у вас нет информации о городе в таблице стран), мы должны обработать все ошибки. Поэтому добавьте следующие строки в модель формы:

def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['city'].queryset = City.objects.none()

        if 'country' in self.data:
            try:
                country_id = int(self.data.get('country'))
                self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
            except (ValueError, TypeError):
                pass  # invalid input from the client; ignore and fallback to empty City queryset
        elif self.instance.pk:
            self.fields['city'].queryset = self.instance.country.city_set.order_by('name')

И добавьте новую функцию для получения отфильтрованных городов, когда пользователь выбирает страну для просмотра перед основным видом. Выглядит это так:

def load_cities(request):
    country_id = request.GET.get('country')
    cities = City.objects.filter(country_id=country_id).order_by('name')
    return render(request, 'city_dropdown_list_options.html', {'cities': cities})

Эта маленькая функция будет запрашивать поле 'страна' в вашей форме и отправлять города с фильтрами. Давайте создадим новый html-файл в вашем каталоге, например 'city_dropdown_list_options.html':

<option value="">---------</option>
{% for city in cities %}
<option value="{{ city.pk }}">{{ city.name }}</option>
{% endfor %}

Добавьте новый URL-адрес в ваш urls.py:

path('ajax/load-cities/', views.load_cities, name='ajax_load_cities')

Теперь пришло время создать запрос AJAXв твоей форме. В приведенном ниже примере я использую jQuery, но вы можете использовать любой каркас JavaScript (или просто обычный JavaScript) для создания асинхронного запроса:

{% block content %}

  <h2>Your Form</h2>

  <form method="post" id="ProfileUpdateForm" data-cities-url="{% url 'ajax_load_cities' %}" novalidate>
    {% csrf_token %}
    <table>
      {{ form.as_table }}
    </table>
    <button type="submit">Save</button>
    <a href="{% url 'whatyouwant' %}">Go!</a>
  </form>

  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script>
    $("#id_country").change(function () {
      var url = $("#ProfileUpdateForm").attr("data-cities-url");  // get the url of the `load_cities` view
      var countryId = $(this).val();  // get the selected country ID from the HTML input

      $.ajax({                       // initialize an AJAX request
        url: url,                    // set the url of the request (= localhost:8000/ajax/load-cities/)
        data: {
          'country': countryId       // add the country id to the GET parameters
        },
        success: function (data) {   // `data` is the return of the `load_cities` view function
          $("#id_city").html(data);  // replace the contents of the city input with the data that came from the server
        }
      });

    });
  </script>

{% endblock %}

Та да! Если вы выберете страну, вы увидите только города, которые отфильтрованы по стране. Это волшебство, верно? Все это я узнал из https://www.simpleisbetterthancomplex.com,, который вы можете искать по следующему названию: «Как реализовать зависимый / цепной выпадающий список с помощью Django?». Удачи, братан!

...