Existe-t-il un moyen de créer un champ qui est un champ de texte, mais avec des choix. que les choix sont les objets existants sur le terrain? Et si le mot passé n'est pas dans les données existantes, ajoutez-le?
J'ai cherché sur tout Internet un champ comme celui-ci ou un exemple de celui-ci, que ce soit dans Django ou dans le framework reste, mais je n'ai pas pu en trouver .
Mon utilisation pour cela, par exemple, serait:
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)
Donc, il pourrait être utilisé lors d'une demande de publication au lieu de déjà saisir la latitude , longitude Il serait extrait de l'objet existant.
3 Réponses :
Au niveau de la base de données, un champ ne peut pas être à la fois un champ de type "choix" et un champ de texte libre.
Ce que vous voulez réellement, c'est une abstraction au-dessus de quelques champs DB cachés
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
Ceci n'est que le premier "niveau DB", mais vous devrez également personnaliser la méthode '.save ()', ajouter un setter avec @ property.setter code >.
Une autre option serait d'écrire cette abstraction au niveau Form
au lieu du niveau Model
.
Cela ressemble un peu à la recherche de la fonction django get_or_create
. Étant donné un modèle comme Point
, vous pouvez l'appeler comme;
obj, created = Point.objects.get_or_create( location_name ='Location', defaults={'latitude': 1, 'longitude': 2}, )
et il renvoie une instance obj
et un booléen créé
qui indique si cet objet est nouveau ou préexistant.
Par conséquent, si vous lui donnez un location_name
existant, il récupérera l'objet existant pour vous, mais si vous donnez-lui un nouveau, il créera un nouvel objet avec ce nom.
En me fondant sur la logique proposée par Timothe ici, j'ai construit une sorte de contournement au problème que j'ai présenté.
Premièrement, le modèle utilisant le modèle Point comme champ de choix:
gdt = return_data_by_name(data=get_known_location(), query=location_name)
le champ gdt n'hérite pas directement du modèle ponctuel, afin que je puisse faire ce qui suit:
Ici, je ne peux pas montrer exactement ce que j'ai fait, mais au début, je viens d'ajouter un autre champ appelé add_gdt, qui est un champ lié au modèle de localisation connu.
Et comme Timothe l'a suggéré, si ce n'est pas le cas, choisissez le champ gdt, si aucun choix n'a été fait sur gdt, puis ajoutez le champ add_gdt à la base de données de l'emplacement connu en tant que champ connexe normal.
Sérialiseur:
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
utilisez gdt ici comme champ de choix, en utilisant le paresseux de django pour créer des noms correspondant au champ du modèle de points, c'est-à-dire que le champ de choix sera composé des noms des points connus dans la base de données.
Comment s'appelle 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()
Récupère les données du qeuryset, sérialise-le, récupère les données et crée un dictionnaire exploitable à partir de celui-ci, avec les éléments suivants:
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')
Enfin, les données utilisées dans la vue, en utilisant la fonction return by name pour obtenir les données.
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)
p>
Jetez un œil ici