Django остальные рамки, поле выбора или текст - PullRequest
1 голос
/ 05 апреля 2020

Есть ли способ создать поле, которое является текстовым полем, но с возможностью выбора. что на выбор оказываются поля существующих объектов? И если переданное слово отсутствует в существующих данных, добавьте его?

Я просматривал все целые числа rnet для такого поля или его примера, либо в Django, либо в остальные рамки, но не смог найти.

Например, я мог бы использовать его:

class Point(models.Model):
    location_name = models.TextField(verbose_name="Location name",
                                     unique=True,
                                     # This would include the existing names.
                                     choices=,
                                     )
    latitude = models.FloatField(name="GDT1Latitude",
                                 verbose_name="GDT 1 Latitude",
                                 unique=False, max_length=255, blank=False,
                                 help_text="Enter the location's Latitude, first when extracting from Google Maps.",
                                 default=DEFAULT_VALUE)
    longitude = models.FloatField(name="GDT1Longitude",
                                  verbose_name="GDT 1 Longitude",
                                  unique=False, max_length=255, blank=False,
                                  help_text="Enter the location's Longitude, second when extracting from Google Maps.",
                                  default=DEFAULT_VALUE)

Таким образом, его можно использовать при отправке запроса вместо того, чтобы уже набирать широту, долготу, из которых он будет получен ожидающий объект.

Ответы [ 3 ]

1 голос
/ 05 апреля 2020

На уровне базы данных поле не может быть как «выбором», так и свободным текстовым полем.

Что вам действительно нужно, так это абстракция поверх пары скрытых полей БД

class Book(models.Model):
   THEME_CHOICES = (
     ('Horror', 'Horror'),
     ('Romance', 'Romance')
   )
   _theme = models.CharField(choices=THEME_CHOICES, max_length=258, null=True, blank=True)
   _other_theme = models.CharField(max_length=258)

   @property
   def theme(self):
      if self._other_theme:
          return self._other_theme
      else:
          return self._theme

Это только первый «уровень БД», но вам также понадобится пользовательский метод .save (), добавив сеттер с @property.setter.

Другой вариант - написать эту абстракцию на уровне Form вместо уровня Model.

0 голосов
/ 04 мая 2020

Опираясь на логику c Тимофей, предложенный здесь, я разработал своего рода обходной путь для поставленной мной проблемы.

Сначала модель, использующая модель Point в качестве поля выбора:

  • Точка была переименована в KnownLocation.

class Mission(models.Model):
    id = models.UUIDField(primary_key=False, default=uuid.uuid4, editable=False)

    mission_name = models.CharField(name='MissionName',
                                    verbose_name="Mission Name",
                                    max_length=255,
                                    blank=False,
                                    help_text="Enter the mission's name",
                                    unique=True,
                                    primary_key=True
                                    # We want to be able to query missions not let them be confused with other missions.
                                    )

    uav_lat = models.FloatField(name="UavLatitude",
                                verbose_name="UAV Latitude",
                                unique=False, max_length=255, blank=False,
                                help_text="Enter the location's Latitude, first when extracting from Google Maps.",
                                default=DEFAULT_VALUE)

    uav_lon = models.FloatField(name="UavLongitude",
                                verbose_name="UAV Longitude",
                                unique=False, max_length=255, blank=False,
                                help_text="Enter the location's Longitude, second when extracting from Google Maps.",
                                default=DEFAULT_VALUE)


    gdt = models.CharField(verbose_name='Locations',
                           max_length=20)

в поле gdt не наследовать напрямую от точечной модели, чтобы я мог сделать следующее:

Здесь я не могу точно показать, что я сделал, но сначала я просто добавил еще одно поле с именем add_gdt, которое связано поле для известной модели местоположения.

И, как предложил Тимоте, если оно не выбрано, выберите поле gdt, если выбор не был сделан для gdt, чем добавьте поле add_gdt к известной базе данных местоположения как обычное связанное поле.

Сериализатор:

from Utils.APIConsts.Custom import get_locations, known_locations_data
from django.utils.functional import lazy
from rest_framework.fields import ChoiceField, DictField
from rest_framework.serializers import HyperlinkedModelSerializer

from .models import Mission


class MissionSerializer(HyperlinkedModelSerializer):
    gdt = ChoiceField(choices=lazy(get_locations, tuple)())
    locations = DictField(known_locations_data)

    class Meta:
        model = Mission
        fields = ('Area', 'gdt', 'MissionName', 'UavLatitude', 'UavLongitude', 'UavElevation',
                  'locations')

здесь используйте gdt в качестве поля выбора, используя ленивый django для создания имен, соответствующих полю точечной модели, то есть поле выбора будет состоять из имен из известных точек в БД.

Как называется get_locations?

Utils.APIConsts.Custom.py:

def get_locations():
    """
    Returns the names of the locations in KnownLocation model.
    """
    return [loc.Name for loc in KnownLocation.objects.all()]


def get_known_location():

    qeuryset = KnownLocation.objects.all()
    serialized_data = KnownLocationSerializer(qeuryset,
                                              many=True)

    locations_data = serialized_data.data
    to_repr_dict = repack(data=locations_data, return_type='dict')
    return to_repr_dict


known_locations_data = get_known_location()

Получить данные из qeuryset, сериализуйте его, ge t данных и создал из них работающий словарь со следующими данными:

def repack(data, return_type=DICT):
    """
    This function receives a list of ordered dictionaries to to unpack and reassign every dict to it's first value.
    data: the data to unpack
    return_type: specify the returned data type; dict or list.
    'dict' is default
    """
    try:
        repacked_dict = {next(iter(d.values())): d for d in data}
        if return_type is DICT:
            return repacked_dict
        elif return_type is LIST:
            repacked_list = list(repacked_dict.items())
            return repacked_list
        raise TypeError('requested return type is not valid.')
    except:
        raise TypeError('data is not a list.')




def return_data_by_name(data, query) -> list:
    """
    This function receive a nested dictionary and matches a given the query to the keys of the dictionary,
    If there's a match, than that match is returned.
    :param dict data:
    :param str query:
    """
    for name, values in data.items():
        if query.lower() in name.lower():
            values = list(values.values())
            return values

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

gdt = return_data_by_name(data=get_known_location(), query=location_name)
0 голосов
/ 05 апреля 2020

Звучит так, будто вы ищете функцию django get_or_create. Учитывая модель типа Point, вы можете назвать ее как;

obj, created = Point.objects.get_or_create(
    location_name ='Location',
    defaults={'latitude': 1, 'longitude': 2},
)

, и она возвращает экземпляр obj и логическое значение created, которое указывает, является ли этот объект новым или уже существующим.

Поэтому, если вы дадите ему существующий location_name, он извлечет существующий объект для вас, однако, если вы дадите ему новый объект, он создаст новый объект с этим именем.

...