You are hereBlogs > jittat's blog > มาสร้าง app สำหรับอัพโหลดรูปกัน (3)
มาสร้าง app สำหรับอัพโหลดรูปกัน (3)
ต่อจากตอนที่แล้วนะครับ
ในตอนนี้เราจะหัดใช้ form ครับ ในตอนต้นเราจะเขียน form แบบธรรมดาเพื่อทดลองใช้ form และทดลองเก็บข้อมูลลงในโมเดล จากนั้นก่อนจะจบเราจะทดลองใช้ ModelForm ซึ่งทำให้การสร้างและใช้งานฟอร์มจากโมเดลทำได้ง่ายมากขึ้น
Django มีโมดูล forms ที่ทำให้การเขียน form ที่ต้องมีการตรวจสอบข้อมูลทำได้สะดวกครับ
การใช้งานหลัก ๆ คือเราไปสร้างคลาสที่เป็นลูกหลานของ forms.Form วัตถุของคลาสดังกล่าว เมื่อนำไปแสดงใน template จะเผยตัวออกเป็น html form ที่สวยงาม จากนั้นเมื่อผู้ใช้ป้อนข้อมูลเข้ามาผ่านทาง form นั้น เราก็สามารถเอาเชื่อมข้อมูลที่ผู้ใช้ส่งมาจาก request เข้ากับวัตถุของ form เพื่อนำไปตรวจสอบความถูกต้องได้ เมื่อตรวจเสร็จเราก็จะมีข้อมูลที่เก็บกวาดแล้วไว้ใช้ (เรียกว่า cleaned_data)
เรามาประกาศ form สำหรับรับรูปกัน เพิ่มการประกาศนี้ไว้ที่ต้นไฟล์ pictures/views.py ครับ
class PictureForm(forms.Form):
title = forms.CharField(max_length=100)
description = forms.CharField(widget=forms.Textarea)
image = forms.ImageField()
สังเกตว่าคล้ายกับการประกาศโมเดลมากครับ ยกเว้นว่าไม่มี TextField แต่เราจะใช้ forms.CharField แทน แล้วระบุว่าในเวลาแสดงผลเป็น html ให้แสดงเป็น textarea โดยกำหนดค่า widget=forms.Textarea เจ้า widget นี่คือวัตถุที่ใช้จัดการแสดงผลแต่ละ field ใน template ครับ
มีฟอร์มแล้วก็ต้องส่งไปแสดงผลที่ template
เราจะให้ผู้ใช้ upload ได้ที่หน้าแรกเลย ดังนั้นไปแก้ฟังก์ชัน index กัน โดยเพิ่มให้มีการสร้างวัตถุของคลาส PictureForm ให้เป็นฟอร์มเปล่า ๆ แล้วโยนไปให้ template แสดงผล ผ่านทางตัวแปรชื่อ form
pics = Picture.objects.all().order_by('title')
form = PictureForm()
return render_to_response("index.html",{ 'pictures': pics,
'form': form })
จากนั้นไปแก้ template index.html จากคราวที่แล้วครับ
โดยเพิ่มส่วน form เข้าไปในตอนต้นครับ
{{ form.as_p }}
<input type="submit" value="Upload"/>
</form>
สังเกตว่าการเขียน tag form ก็เขียนตามปกติ (อย่าลืม enctype="multipart/form-data") แต่ว่าส่วนที่แสดงผลฟิลด์ต่าง ๆ ในฟอร์มเราสั่งให้แสดง form.as_p ได้เลย ลองเรียก python manage.py runserver ขึ้นมาดูหน้าตากันนะครับ สังเกตว่าฟอร์มจะเบี้ยว ๆ หน่อย ถ้าเข้าไปดูที่ html จะเห็นว่าจาก form.as_p จะได้ html ประมาณนี้ครับ
<p><label for="id_description">Description:</label> <textarea id="id_description" rows="10" cols="40" name="description"></textarea></p>
<p><label for="id_image">Image:</label> <input type="file" name="image" id="id_image" /></p>
ถ้าเราไม่ชอบเบี้ยว ๆ ก็แก้ให้แสดงเป็นตารางก็ได้ครับ แก้เป็น
<table>
{{ form.as_table }}
</table>
<input type="submit" value="upload"/>
</form>
พอได้หน้า template แล้ว เราก็ไปเขียนตัวจัดการกับการ upload เลยครับ
เราจะเขียนฟังก์ชัน upload ให้จัดการกับ url upload/ ดังนั้นไปเพิ่มบรรทัดด้านล่างในไฟล์ urls.py
ส่วนโค้ดของ upload อยู่ด้านล่างครับ
if request.method == 'POST':
# ผูก form เข้ากับข้อมูลที่ได้รับมา
form = PictureForm(request.POST, request.FILES)
if form.is_valid(): # ตรวจสอบข้อมูลในฟอร์ม
# เอาค่าที่เก็บกวาดแล้วมาใช้
title = form.cleaned_data['title']
desc = form.cleaned_data['description']
imagefile = form.cleaned_data['image']
# สร้างรูปใหม่ แล้วเก็บลงฐานข้อมูล
new_picture = Picture()
new_picture.title = title
new_picture.description = desc
new_picture.image.save(imagefile.name,imagefile)
new_picture.save()
return HttpResponseRedirect('/')
หลัก ๆ คือถ้าผู้ใช้ส่ง request มาแบบ POST (คือส่งฟอร์มมา) เราก็จะผูกวัตถุของ PictureForm เข้ากับข้อมูลที่ได้รับ อันนี้พิเศษหน่อยที่เราต้องมี request.FILES ด้วย เพราะว่าเรามีการส่งไฟล์มา
จากนั้นเรียกเมท็อด is_valid เพื่อตรวจสอบ/เก็บกวาดข้อมูลใน form ถ้าถูกแล้วเราจะได้ข้อมูลไว้ใช้เป็น dictionary ชื่อ cleaned_data (ในการตรวจสอบความถูกต้องนี้เราสามารถเขียนเพิ่มเติมเกี่ยวกับเงื่อนได้)
ตอนท้าย ๆ เป็นการสร้างวัตถุใหม่ในโมเดล แล้วเก็บลงฐานข้อมูลครับ เราเริ่มสร้างวัตถุของคลาส Picture แล้วก็กำหนดค่าให้กับแต่ละฟิลด์เลย ที่ลำบากหน่อยคือส่วนของการเก็บรูป เพราะว่าเราต้องเก็บลงไปในที่ที่ตั้งไว้ ในขณะค่าในตัวแปร image ที่ได้มาจะอิงกับข้อมูลที่ผู้ใช้ป้อน ซึ่งอาจจะเก็บอยู่ในไดเร็กทอรีไฟล์ชั่วคราว ดังนั้นจะเอามาใช้ก็ต้องสั่ง save เอาเองที่ฟิลด์นั้นครับ (ในเอกสารเก่าหน่อยจะให้สั่ง save_FIELD_file แต่มัน deprecated ไปแล้ว)
สุดท้ายเราสั่ง HttpResponseRedirect ให้ browser redirect กลับไปหน้าแรก จะสั่งฟังก์ชันนี้ได้อย่าลืมใส่บรรทัดด้านล่างไว้ตรงหัว views.py นะครับ
เท่านี้ก็เรียบร้อยครับ ทดลองเล่นได้ครับ
ที่เราทดลองมานี่เป็นการทำแบบอ้อม ๆ ครับเพื่อแสดงให้เห็นการใช้งาน Form อย่างง่ายครับ แต่จริง ๆ แล้ว ถ้าเราต้องการสร้างฟอร์มจากโมเดล เรามีคลาสช่วยเหลืออย่าง ModelForm ทำให้เขียนง่ายขึ้นมากครับ ด้านล่างเป็น views.py ในส่วนประกาศ PictureForm และฟังก์ชัน upload ที่เขียนโดยใช้ ModelForm ครับ
class Meta:
model = Picture # สร้าง form จากโมเดล Picture
def upload(request):
if request.method == 'POST':
# ผูกฟอร์มเข้ากับข้อมูล
form = PictureForm(request.POST, request.FILES)
if form.is_valid(): # ทดสอบความถูกต้อง
form.save() # เก็บ
return HttpResponseRedirect('/')
ส่วนที่หายไปก็คือการประกาศฟิลด์ตามโมเดล และส่วนการจัดการเรื่อง save ครับ
ที่นี้ก่อนจบมาเก็บกวาดกันต่ออีกสักนิดครับ
สังเกตว่าในโมเดลเรามีฟิลด์ description แต่ไม่ได้เอาไปแสดงผลเลย ถ้าต้องการแสดงก็แก้ส่วน template โดยเพิ่มให้แสดง {{ picture.description }} เข้าไปนะครับ ทีนี้ ถ้าเราพิมพ์หลาย ๆ บรรทัดแล้วเราต้องการให้มีการแปลงเครื่องหมายขึ้นบรรทัดใหม่ให้เป็น <br> ให้ใส่เป็น {{ picture.description|linebreaks }} แทน ส่วน |linebreaks เป็น filter ให้เราปรับแต่งการแสดงผลของตัวแปรได้ครับ
ปรับเอาตามชอบเลยนะครับ ตัวอย่างด้านล่างนี่แสดงรูป (ย่อเหลือความกว้าง 100) แล้วแสดงคำอธิบายข้าง ๆ ครับ
<h3>{{ picture.title }}</h3>
<table>
<tr><td>
<img src="{{ picture.image.url }}" width="100"/>
</td><td>
{{ picture.description|linebreaks }}
</td></tr>
</table>
{% endfor %}
ส่วนของ tutorial การเขียน app เพื่ออัพโหลดรูปคงจบไว้คร่าว ๆ แค่นี้ครับ (อาจมีตอนต่อไปอีกได้ เกี่ยวกับการทำ user และทำเรื่องการ follow)
เอกสารเพิ่มเติม
- ดูรายละเอียดเกี่ยวกับ file upload
- เอกสาร ModelForm
- jittat's blog
- Login or register to post comments
