React für Backend-Entwickler (Teil 1)

Das GitHub-Repository für diesen Blogbeitrag ist https://github.com/gbozee/react-for-backend-dev.

React ist eine Front-End-Webbibliothek, die verwendet werden kann, um umfangreiche Front-End- oder Single-Page-Anwendungen (SPA) zu erstellen, aber das bedeutet nicht, dass sie nicht neben einem bestehenden Back-End-Web-Framework verwendet werden kann.

Ich würde sagen, dass dies eine der Kernstärken von React ist. Mit seiner Leistungsfähigkeit können wir unsere langweiligen Back-End-Web-Apps/Websites mit Leichtigkeit aufpeppen.

In den nächsten Blogbeiträgen werde ich einige Bereiche besprechen, in denen wir React verwenden können, um die vom Django-Python-Webframework bereitgestellten Funktionen zu erweitern. Dies kann auch auf jedes andere serverseitige Webframework angewendet werden.

Unser Beispielszenario besteht aus der Implementierung eines Formsets in Django. Ein Schmerzpunkt, den Django nicht löst, ist die Möglichkeit, ein neues Formular dynamisch zu einem Django-Formularsatz im Browser hinzuzufügen.

Eine Lösung dafür ist die Verwendung von jQuery, aber es ist in der Regel schwierig, ihr zu folgen. Dies ist ein perfekter Anwendungsfall, um React auszuprobieren und zu sehen, wie es zur Lösung dieses Problems beiträgt. In dem Szenario, das wir gemeinsam untersuchen, stellen wir sicher, dass die folgenden Anforderungen erfüllt sind:

  1. Wir können dem Formset erfolgreich ein neues Formular hinzufügen.
  2. Wir können Validierungen erzwingen, die vom Formset auf dem Server auferlegt werden.

Zunächst erstellen wir eine virtuelle Umgebung für unsere Web-App. Ich verwende git bash für Windows, um sicherzustellen, dass die Umgebung auf allen Betriebssystemplattformen so ähnlich wie möglich ist.

$ mkdir react_django
$ cd react_django
$ virtualenv venv
$ source venv/Scripts/activate # in mac and linux source venv/bin/activate
$ pip install django
$ django-admin startproject formset_in_react
$ cd formset_in_react
$ python formset_in_react/manage.py runserver

Mit dem oben Gesagten sollten wir ein funktionierendes Django-Projekt am Laufen haben.
Lassen Sie uns unsere erstellen index view das unser Formset in der beherbergen wird urls.py bereitgestellt von Django.
urls.py

Ich werde ein einzelnes Formular in Django erstellen, auf dem unser Formset basieren wird. Dieses Formular besteht aus zwei einfachen Feldern, school und degree.

From Django import forms:

class EducationForm(forms.Form):
    school = forms.CharField()
    course = forms.CharField()

EducationFormset = forms.formset_factory(EducationForm,max_num=2)

Das Formularset stellt sicher, dass die maximale Anzahl von Formularen nicht mehr als zwei beträgt.

Bevor wir uns in das React-Land wagen, sehen wir uns den generierten HTML-Code aus dem von Django generierten Formset an und verbinden alles in unserer Ansicht.

Unter Verwendung der interaktiven Python-Shell erhalten wir den folgenden HTML-Code, der aus dem Formset generiert wird.

$ python formset_in_react/manage.py shell
>>> from formset_in_react.urls import EducationFormset
>>> sample_form = EducationFormset()
>>> sample_form.as_ul()
u'<input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS
" /><input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS" /><input type="hidden" name="form-MAX_NUM_FORMS" value="2" id="id_form-MAX_
NUM_FORMS" />\n<li><label for="id_form-0-school">School:</label> <input type="text" name="form-0-school" id="id_form-0-school" /></li>\n<li><label for="id_form-0-cour
se">Course:</label> <input type="text" name="form-0-course" id="id_form-0-course" /></li>`

Wenn wir die obige Ausgabe verschönern, erhalten wir Folgendes:

<input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" />

<input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS
" />

<input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS" />

<input type="hidden" name="form-MAX_NUM_FORMS" value="2" id="id_form-MAX_
NUM_FORMS" />

<li>
  <label for="id_form-0-school">School:</label> 
  <input type="text" name="form-0-school" id="id_form-0-school" />
</li>
<li>
<label for="id_form-0-course">Course:</label> 
  <input type="text" name="form-0-course" id="id_form-0-course" />
</li>`

Aus der Ausgabe können wir ersehen, dass der generierte HTML-Code viele versteckte Felder enthält, aber keine Schaltfläche/Link, der uns hilft, ein neues Formular im Formset zu erstellen. Wir sollen die Informationen in den versteckten Formularfeldern mit einer JavaScript-Bibliothek verwenden, um die zu implementieren add Funktionalität.

Lassen Sie uns unsere Ansicht und HTML einrichten und versuchen, unser Formular ein wenig schöner zu machen Bootstrap.

Das Ganze urls.py Inhalt:
urls.PNG

Ich werde eine Konfigurationsänderung an den Djangos vornehmen settings.py damit Django unsere finden kann templates Lage.

settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates")], # the change
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Beobachten Sie, wo ich die erstellt habe templates Mappe:
urls.PNG

Die Indexseite sollte jetzt im Browser angezeigt werden und zugänglich sein.

Der Inhalt der index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href=" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    
    <!-- Optional theme -->
    <link rel="stylesheet" href=" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
    <title>Formset with django</title>
    <style>
        .margin-top{
            margin-top: 50px;
        }   
    </style>
</head>
<body>
    <div class="container margin-top">
        <div class="row">
            <div class="col-md-10 col-md-offset-1">
                <form method="post">
                    {% csrf_token %}
                    <div id="formset">
                        {{form.as_ul}}
                    </div>
                </form>
            </div>
        </div>
    </div>
</body>
</html>

Wir können hier sehen, dass ich eine erstellt habe id bevor Sie das von Django bereitgestellte Standard-Rendering des Formsets verwenden. Ich werde das entführen id mit React und ersetzen Sie es durch das modifizierte Formset.

Wir sollten Folgendes in unserem Browser erhalten:
urls.PNG

Jetzt ist es an der Zeit, React einzurichten. Ich werde den einfachen Ansatz verwenden, der in meinem letzten Blogbeitrag Erste Schritte mit React the Easy Way erklärt wurde.

index.html

<head>
...
<script src=" crossorigin="anonymous"></script>
    <script src=" crossorigin="anonymous"></script>
    <script src=" crossorigin="anonymous"></script>
    <script src="
    <script type="text/babel">
    // Our source code would go here
    </script>
</head>

Erinnern Sie sich an den ursprünglichen Inhalt des Formsets, von dem wir über das Terminal erfahren haben. Wir werden eine React-Komponente erstellen, die diese enthält, bevor wir Änderungen vornehmen.

// Our source code would go here 
const React = window.React; 
const ReactDOM = window.ReactDOM; 
const createReactClass = window.createReactClass;
const Formset = createReactClass({ 
  render(){ 
    return (
      <div>
       <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS" />
       <input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" />
       <input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS" />
       <input type="hidden" name="form-MAX_NUM_FORMS" value="2" id="id_form-MAX_NUM_FORMS" />
       <li>
       	<label for="id_form-0-school">School:</label>
       	<input type="text" name="form-0-school" id="id_form-0-school" />
       </li>
       <li>
       	<label for="id_form-0-course">Course:</label>
       	<input type="text" name="form-0-course" id="id_form-0-course" />
       </li>
     </div>
    )} 
 })
 ReactDOM.render(<Formset/>,document.getElementById('formset'))
 </script>

Beim Aktualisieren unserer Seite sollten wir immer noch genau dieselbe Ansicht erhalten, aber jetzt haben wir die Kontrolle über die Darstellung des Formulars mit React übernommen. Unsere benutzerdefinierte React-Komponente wird in die injiziert formset id-Element.

Vereinfachen wir unsere Komponente ein wenig.

 const Form = createReactClass({
 render(){
 	return (
      <div>
        <li>
          <label for="id_form-0-school">School:</label>
          <input type="text" name="form-0-school" id="id_form-0-school" />
        </li>
        <li>
          <label for="id_form-0-course">Course:</label>
          <input type="text" name="form-0-course" id="id_form-0-course" />
        </li>
      </div>
      )}
  })
  const Formset = createReactClass({ 
  	render(){ 
    	const management_data = [
          {name: "TOTAL", value:1,},
          {name: "INITIAL", value: 0},
          {name: "MIN_NUM", value: 0},
          {name: "MAX_NUM",value: 2}
        ]
        return (
        	<div>
              {management_data.map((d,index)=>
              	<input key={`management-${index}`} 
              		type="hidden" name={`form-${d.name}_FORMS`} 
              		value={d.value} id={`id_form-${id.name}_FORMS`} />
              )}
              <Form />
            </div>
        )} 
   })

Ich zog das eigentliche Formular in eine separate Komponente und vermied es, alle versteckten Felder zu duplizieren. Jetzt kann ich die verschieben management_data aus der Komponente und übergeben Sie es als a props zum Formset Komponente. Es sieht aus wie das:

...
 const Formset = createReactClass({ 
 	render(){ 
    	return (
          <div>
          	{this.props.management_data.map((d,index)=>
          		<input key={`management-${index}`} 
          			type="hidden" name={`form-${d.name}_FORMS`} 
          			value={d.value} id={`id_form-${id.name}_FORMS`} />
          	)}
          	<Form />
          </div>
       )} 
 })
        
const management_data = [
  {name: "TOTAL", value:1,},
  {name: "INITIAL", value: 0},
  {name: "MIN_NUM", value: 0},
  {name: "MAX_NUM",value: 2}
  ]
  ReactDOM.render(
  	<Formset managemend_data={managemend_data} />,
  	document.getElementById('formset')
  )

Zurück zu unserem Form Komponente, aufgrund der Art und Weise, wie das Django-Formularset tatsächlich Formset-Daten benötigt, folgt die ID jedes Formularfelds einer bestimmten Konvention, id_form-0-school bei dem die 0 stellt Felder in derselben Formularinstanz dar. Das können wir ausnutzen.

Wir müssen die Anzahl der Formulare verfolgen, die wir erfolgreich hinzugefügt haben, damit wir einen lokalen Staat zu unserem hinzufügen können Formset Komponente.

urls.PNG

Oben haben wir einen lokalen Staat namens erstellt noOfForms und setzen Sie den Standardwert auf 1. Dann in der render -Methode der Formset-Komponente haben wir dynamisch ein Array basierend auf dem aktuellen lokalen Zustand erstellt und damit gefüllt undefined durch die Verwendung der fill -Methode auf dem Array-Prototyp und dann verwendet map um das individuelle Formular zurückzusenden.

Ich habe auch eine Schaltfläche hinzugefügt, die für das Hinzufügen eines neuen Formulars zum Bildschirm verantwortlich ist. Im Moment bringt es nichts.

In dem Form Komponente erhalte ich den Index des Formulars, das vom übergeordneten Element übergeben wird Formset und verwenden Sie diese, um die zu generieren id und name von Feldern im Formular, damit der Django-Weg nicht gebrochen wird.

Lassen Sie uns die Implementierung abschließen, indem wir die Schaltfläche implementieren add und remove Aktionen.

In dem Form Komponente, ich erwarte a props das würde bestimmen, ob ich das bestimmte Formular aus dem übergeordneten Element entfernen möchte oder nicht, sowie die Aktion, die ausgeführt werden soll, wenn das remove Schaltfläche angeklickt wird.

const Form = createReactClass({
    removeForm(e){
         e.preventDefault() 
        this.props.removeForm(this.props.index);
    },
  render(){
      const school = `form-${this.props.index}-school`
      const course = `form-${this.props.index}-course`
      	return (
        	<div>
            	<li>
                  <label for={`id_${school}`}>School:</label>
                  <input type="text" name={school} id={`id_${school}`} />
                </li>
                <li>
                  <label for={`id_${course}`}>Course:</label>
                  <input type="text" name={course} id={`id_${course}`} />
                </li>
                {this.props.displayRemoveButton ? 
                	<button onClick={this.removeForm}>Remove Form</button>
                	: null}
           </div>
     )}
  })

Das Remove Form Knopf hat ein onClick Ereignishandler, der eine Methode innerhalb der aufgerufenen Komponente aufruft removeForm, die die Logik zum Aufrufen der übergebenen Funktion vom übergeordneten Element enthält, das weiß, wie das Formular gelöscht wird. Wir verhindern, dass standardmäßige Browserereignisaktionen durch Aufrufen ausgeführt werden e.preventDefaultda wir nicht möchten, dass unser Formular versehentlich an den Server gesendet wird.

Wir müssen dann die Methode zum Entfernen eines Formulars aus dem übergeordneten Formular erstellen Formset.

const Formset = createReactClass({ 
  getInitialState(){
      return {
      	noOfForms:1
    }
  },
    addNewForm(e){
      e.preventDefault()
      this.setState({noOfForms:this.state.noOfForms+1})
    },
    removeForm(index){
    	this.setState({noOfForms: this.state.noOfForms-1})
    },
    render(){ 
    	return (
        	<div>
            	{this.props.management_data.map((d,index)=>
                	<input key={`management-${index}`} 
                    	type="hidden" name={`form-${d.name}_FORMS`} 
                        value={d.value} id={`id_form-${d.name}_FORMS`} />
                 )}
                 {Array(this.state.noOfForms).fill().map((form,index)=>
                 	<Form key={`form-${index}`} index={index} 
                    	displayRemoveButton={this.state.noOfForms > 1} 
                        removeForm={this.removeForm} />
                 )}
                 <button onClick={this.addNewForm}>Add new form</button>
       		</div>
         )} 
   })

Okay, unsere Form Komponente erhält nun weitere Requisiten. Das displayRemoveButton props bestimmen, ob der removeButton auf dem Formular angezeigt wird oder nicht. Das removeForm Props übernehmen die Funktion, die vom untergeordneten Formular aufgerufen werden soll, wenn auf die Schaltfläche removeForm geklickt wird.

Wenn das obige implementiert und der Browser aktualisiert ist, sollten wir in der Lage sein, Formulare zum Formset hinzuzufügen und zu entfernen. Wir könnten Elemente mithilfe der Browser-Entwicklungstools untersuchen, um sicherzustellen, dass die id und name for-Feld in jedem Formular haben den Index des Formulars und sind konsistent mit der Django-Implementierung.

Im nächsten Blogbeitrag werden wir uns damit befassen, wie durchgesetzt werden kann, dass die vom Server benötigte maximale Anzahl von Formularen nicht überschritten wird, und sicherzustellen, dass alles wie erwartet funktioniert, wenn das Formular schließlich gesendet wird. Hab einen schönen Tag!

Das GitHub-Repository für diesen Beitrag wird gehostet unter https://github.com/gbozee/react-for-backend-dev.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *