简体   繁体   中英

Saving Django Form Data

I am learning Django forms and am trying to save form data. I have a working form, but I can't figure out to 'do' anything with the data entered on the form. Specifically, I am trying to do the following two things:

First , once the user submits the form, load a new page that states: "You searched for 'X'".

Second , have the form data interact with an existing database. Specifically, I have a model called 'Hashtag' that has two attributes: 'search_text' and 'locations'. I think the process would work as follows:

  • Send X to the Model ('Hashtag'),
  • If X is equal to an existing hashtag.search_text object in the database, then return a page with: "The following are the locations for 'X': 'Y'
  • If X doesn't equal an existing hashtag.search_text object in the database, then return a page with: "The following are the locations for 'X': no locations found".

Where,

X = user-inputted form data

Y = hashtag.locations.all() in a list

Thus far, I have the below:

models.py

from django.db import models


class Hashtag(models.Model):
    """
    Model representing a specific hashtag search. The model contains two attributes:
        1) a search_text (eg 'trump') for which there will be only one for database entry (the row),
        2) a list of locations (eg ['LA, CA', 'LA, CA', 'NY, NYC', 'London, UK', 'London, United Kingdom']) for which there may be 0+ per search_text.
    """

    search_text = models.CharField(max_length=140, primary_key=True)
    locations = models.TextField()

    def __str__(self):
        """ String for representing the Model object (search_text) """
        return self.search_text

    def display_locations(self):
        """ Creates a list of the locations """
        # ISSUE: insert correct code, something like: return '[, ]'.join(hastagsearch.location_list for location in self.location.all())
        pass

forms.py

from django import forms
from django.forms import ModelForm

from .models import Hashtag


class SearchHashtagForm(ModelForm):
    """ ModelForm for user to search by hashtag """

    def clean_hashtag(self):
        data = self.cleaned_data['search_text']
        # Check search_query doesn't include '#'. If so, remove it.
        if data[0] == '#':
            data = data[1:]
        # return the cleaned data
        return data

    class Meta:
        model = Hashtag
        fields = ['search_text',]
        labels = {'search_text':('Hashtag Search'), }
        help_texts = { 'search_text': ('Enter a hastag to search.'), }

views.py

from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Hashtag
from .forms import SearchHashtagForm


def hashtag_search_index(request):
    """ View for index page for user to input search query """
    hashtag_search = get_object_or_404(Hashtag)

    # If POST, process Form data
    if request.method == 'POST':
        # Create a form instance and populate it with data from request (binding):
        form = SearchHashtagForm(request.POST)
        # Check if form is valid
        if form.is_valid():
            # process the form data in form.cleaned_data as required
            hashtag_search.search_text = form.cleaned_data['search_text']
            # the reason we can use .save() is because we associated the form with the model as a ModelForm
            hashtag_search.save()
            # redirect to a new URL
            return HttpResponseRedirect(reverse('mapping_twitter:hashtag_search_query'))
    # If GET (or any other method), create the default form
    else:
        form = SearchHashtagForm()

    context = {'hashtag_search':hashtag_search, 'form':form}
    return render(request, 'mapping_twitter/hashtag_search_query.html', context)

I am considering that a potential way to achieve this is to create another model and save the user-inputted form data there. I am wondering whether that is correct, and how that solution could be used to achieve the Second stated goal above :)

Thanks and apologies in advance if my explanation is a mess/plain wrong :/

EDIT

The EDIT below has made the following changes:

  • Updated models.py as per @Wiggy A.'s answer,
  • Updated views.py to include def results()
  • Included a link to the repo on GitHub.

models.py

from django.db import models


class Location(models.Model):
    """ Model representing a Location, attached to Hashtag objects through a
    M2M relationship """

    name = models.CharField(max_length=140)

    def __str__(self):
        return self.name

class Hashtag(models.Model):
    """ Model representing a specific Hashtag serch, containing two attributes:
        1) A `search_text` (fe 'trump'), for which there will be only one per
        database entry,
        2) A list of `locations` (fe ['LA, CA', 'NY, NYC']), for which there
        may be any number of per `search_text` """

    search_text = models.CharField(max_length=140, primary_key=True)
    locations = models.ManyToManyField(Location, blank=True)

    def __str__(self):
        """ String for representing the Model object (search_text) """
        return self.search_text

    def display_locations(self):
        """ Creates a list of the locations """
        # Return a list of location names attached to the Hashtag model
        return self.locations.values_list('name', flat=True).all()

views.py

...
def results(request):
    """ View for search results for `locations` associated with user-inputted `search_text` """

    search_text = hashtag_search
    location_list = Hashtag.display_locations()

    context = {'search_text':search_text, 'location_list':location_list}

    return render(request, 'mapping_twitter/results.html')

The full repo can be found here: https://github.com/darcyprice/Mapping-Data

EDIT 2

The EDIT below makes the following changes:

  • Updated views.py to include @Wiggy A.'s suggested amendment of def results()
  • Included a copy of the ERROR message received due to the updated changes.

Although I copied directly from the Mozilla tutorial ( https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms ), I suspect that the line: hashtag_search.search_text = form.cleaned_data['search_text'] doesn't correctly store hashtag_search .

ERROR

NameError at /search_query/
name 'hashtag_search' is not defined
Request Method: POST
Request URL:    http://ozxlitwi.apps.lair.io/search_query/
Django Version: 2.0
Exception Type: NameError
Exception Value:    
name 'hashtag_search' is not defined
Exception Location: /mnt/project/mapping_twitter/views.py in hashtag_search_index, line 24
Python Executable:  /mnt/data/.python-3.6/bin/python
Python Version: 3.6.5
Python Path:    
['/mnt/project',
 '/mnt/data/.python-3.6/lib/python36.zip',
 '/mnt/data/.python-3.6/lib/python3.6',
 '/mnt/data/.python-3.6/lib/python3.6/lib-dynload',
 '/usr/local/lib/python3.6',
 '/mnt/data/.python-3.6/lib/python3.6/site-packages']

views.py

def hashtag_search_index(request):
    """ View for index page for user to input search query """

    # If POST, process Form data
    if request.method == 'POST':
        # Create a form instance and populate it with data from request (binding):
        form = SearchHashtagForm(request.POST)
        # Check if form is valid
        if form.is_valid():
            hashtag_search.search_text = form.cleaned_data['search_text']
            hashtag_search.save()
            # redirect to a new URL
            return HttpResponseRedirect(reverse('mapping_twitter:results'))

    # If GET (or any other method), create the default form
    else:
        form = SearchHashtagForm()

    context = {'hashtag_search':hashtag_search, 'form':form}
    return render(request, 'mapping_twitter/hashtag_search_index.html', context)


def results(request):
    """ View for search results for `locations` associated with user-inputted `search_text` """

    search_text = hashtag_search
    location = get_object_or_404(Hashtag, search_text=search_text)
    location_list = location.display_locations()

    context = {'search_text':search_text, 'location_list':location_list}

    return render(request, 'mapping_twitter/results.html', context)

Turn the locations attribute into a M2M field. That sounds like what you need here. Keep in mind that this is untested code.

models.py

from django.db import models


class Location(models.Model):
    """ A model representing a Location, attached to Hashtag objects through a Many2Many relationship """
    name = models.CharField(max_length=140)

    def __str__(self):
        return self.name


class Hashtag(models.Model):
    """
    Model representing a specific hashtag search. The model contains two attributes:
        1) a search_text (eg 'trump') for which there will be only one for database entry (the row),
        2) a list of locations (eg ['LA, CA', 'LA, CA', 'NY, NYC', 'London, UK', 'London, United Kingdom']) for which there may be 0+ per search_text.
    """

    search_text = models.CharField(max_length=140, primary_key=True)
    locations = models.ManyToManyField(Location)

    def __str__(self):
        """ String for representing the Model object (search_text) """
        return self.search_text

    def display_locations(self):
        """ Creates a list of the locations """
        # This will return a list of location names attached to the Hashtag model
        return self.locations.values_list('name', flat=True).all()

views.py

...
def results(request):
    """ View for search results for `locations` associated with user-inputted `search_text` """

    search_text = hashtag_search
    location = get_object_or_404(Hashtag, search_text=search_text)
    location_list = location.display_locations()

    context = {'search_text':search_text, 'location_list':location_list}

    return render(request, 'mapping_twitter/results.html')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM