Why file extension validator is not working properly in django? - django

I am trying to add a file extension validator in the Filefield of my model.
But when I am adding a different extension file through my serializer it's adding the extensions I didn't put in my validators.py
Here is the code so far
# validators.py
def validate_file_extension(value):
import os
from django.core.exceptions import ValidationError
ext = os.path.splitext(value.name)[1] # [0] returns path+filename
valid_extensions = ["png", "jpg", "jpeg", # for images
"pdf", "doc", "docx", "txt", # for documents
"mp3", "aac", "m4a", "mp4", "ogg"] # for audios
if not ext.lower() in valid_extensions:
raise ValidationError('Unsupported file extension.')
#models.py
class QuestionFile(models.Model):
question = models.ForeignKey(
Question, on_delete=models.CASCADE, related_name='files', null=True, blank=True)
FILE_TYPE = (
('NONE', 'NONE'),
('IMAGE', 'IMAGE'),
('DOCUMENT', 'DOCUMENT'),
('AUDIO', 'AUDIO'),
)
file_type = models.CharField(
choices=FILE_TYPE, max_length=50, null=True, blank=True)
file = models.FileField(
'files', upload_to=path_and_rename, max_length=500, null=True, blank=True, validators=[validate_file_extension])
def __str__(self):
return str(self.question)
and here is the output in my serializer for this model
"id": ..,
"file": "<filepath>/c-f479b7453519484b827dbc0051bd9a64.html",
"file_type": ..,
"question": ..
As it's visible, though .html extension is not added in the validators.py still it's uploading.
Did I make any mistakes in my code?
I am unable to figure out that.
If any other information is needed let me know, please.
Thanks

Simple error, Your list should be like this:
valid_extensions = ['.jpg', '.png', '.mp3']
and when you use:
import os
text = "/path/to/file/file.jpg"
name = os.path.splitext(text)
then
if name[1].lower() in valid_extensions:
pass

Related

Upload file at Wagtail bakerydemo

I can't make file upload form field work. My references are:
https://github.com/lb-/bakerydemo/blob/stack-overflow/61289214-wagtail-form-file-upload/bakerydemo/base/models.py
https://dev.to/lb/image-uploads-in-wagtail-forms-39pl
The field was added to admin and appear correctly on my site, but when I try to send the form:
if the field is required, the form is reloaded without any error
if the field is not required, the form is sent but the file is not uploaded
What am I doing wrong? I've been working on this for 3 days and can't find any error message pointing what's wrong. When I get some error message it's always too generic.
Please help! :)
My models.py inside bakerydemo/bakerydemo/base
from __future__ import unicode_literals
import json
from os.path import splitext
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils.html import format_html
from django.urls import reverse
from django import forms
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from wagtail.admin.edit_handlers import (
FieldPanel,
FieldRowPanel,
InlinePanel,
MultiFieldPanel,
PageChooserPanel,
StreamFieldPanel,
)
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Collection, Page
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormSubmission, AbstractFormField, FORM_FIELD_CHOICES
from wagtail.contrib.forms.views import SubmissionsListView
from wagtail.images import get_image_model
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.images.fields import WagtailImageField
from wagtail.search import index
from wagtail.snippets.models import register_snippet
from .blocks import BaseStreamBlock
from django.core.validators import ValidationError
import logging
#register_snippet
class People(index.Indexed, ClusterableModel):
"""
A Django model to store People objects.
It uses the `#register_snippet` decorator to allow it to be accessible
via the Snippets UI (e.g. /admin/snippets/base/people/)
`People` uses the `ClusterableModel`, which allows the relationship with
another model to be stored locally to the 'parent' model (e.g. a PageModel)
until the parent is explicitly saved. This allows the editor to use the
'Preview' button, to preview the content, without saving the relationships
to the database.
https://github.com/wagtail/django-modelcluster
"""
first_name = models.CharField("First name", max_length=254)
last_name = models.CharField("Last name", max_length=254)
job_title = models.CharField("Job title", max_length=254)
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
panels = [
MultiFieldPanel([
FieldRowPanel([
FieldPanel('first_name', classname="col6"),
FieldPanel('last_name', classname="col6"),
])
], "Name"),
FieldPanel('job_title'),
ImageChooserPanel('image')
]
search_fields = [
index.SearchField('first_name'),
index.SearchField('last_name'),
]
#property
def thumb_image(self):
# Returns an empty string if there is no profile pic or the rendition
# file can't be found.
try:
return self.image.get_rendition('fill-50x50').img_tag()
except: # noqa: E722 FIXME: remove bare 'except:'
return ''
def __str__(self):
return '{} {}'.format(self.first_name, self.last_name)
class Meta:
verbose_name = 'Person'
verbose_name_plural = 'People'
#register_snippet
class FooterText(models.Model):
"""
This provides editable text for the site footer. Again it uses the decorator
`register_snippet` to allow it to be accessible via the admin. It is made
accessible on the template via a template tag defined in base/templatetags/
navigation_tags.py
"""
body = RichTextField()
panels = [
FieldPanel('body'),
]
def __str__(self):
return "Footer text"
class Meta:
verbose_name_plural = 'Footer Text'
class StandardPage(Page):
"""
A generic content page. On this demo site we use it for an about page but
it could be used for any type of page content that only needs a title,
image, introduction and body field
"""
introduction = models.TextField(
help_text='Text to describe the page',
blank=True)
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
)
body = StreamField(
BaseStreamBlock(), verbose_name="Page body", blank=True
)
content_panels = Page.content_panels + [
FieldPanel('introduction', classname="full"),
StreamFieldPanel('body'),
ImageChooserPanel('image'),
]
class GalleryPage(Page):
"""
This is a page to list locations from the selected Collection. We use a Q
object to list any Collection created (/admin/collections/) even if they
contain no items. In this demo we use it for a GalleryPage,
and is intended to show the extensibility of this aspect of Wagtail
"""
introduction = models.TextField(
help_text='Text to describe the page',
blank=True)
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Landscape mode only; horizontal width between 1000px and '
'3000px.'
)
body = StreamField(
BaseStreamBlock(), verbose_name="Page body", blank=True
)
collection = models.ForeignKey(
Collection,
limit_choices_to=~models.Q(name__in=['Root']),
null=True,
blank=True,
on_delete=models.SET_NULL,
help_text='Select the image collection for this gallery.'
)
content_panels = Page.content_panels + [
FieldPanel('introduction', classname="full"),
StreamFieldPanel('body'),
ImageChooserPanel('image'),
FieldPanel('collection'),
]
# Defining what content type can sit under the parent. Since it's a blank
# array no subpage can be added
subpage_types = []
class HomePage(Page):
"""
The Home Page. This looks slightly more complicated than it is. You can
see if you visit your site and edit the homepage that it is split between
a:
- Hero area
- Body area
- A promotional area
- Moveable featured site sections
"""
# Hero section of HomePage
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Homepage image'
)
hero_text = models.CharField(
max_length=255,
help_text='Write an introduction for the bakery'
)
hero_cta = models.CharField(
verbose_name='Hero CTA',
max_length=255,
help_text='Text to display on Call to Action'
)
hero_cta_link = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
verbose_name='Hero CTA link',
help_text='Choose a page to link to for the Call to Action'
)
# Body section of the HomePage
body = StreamField(
BaseStreamBlock(), verbose_name="Home content block", blank=True
)
# Promo section of the HomePage
promo_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Promo image'
)
promo_title = models.CharField(
null=True,
blank=True,
max_length=255,
help_text='Title to display above the promo copy'
)
promo_text = RichTextField(
null=True,
blank=True,
help_text='Write some promotional copy'
)
# Featured sections on the HomePage
# You will see on templates/base/home_page.html that these are treated
# in different ways, and displayed in different areas of the page.
# Each list their children items that we access via the children function
# that we define on the individual Page models e.g. BlogIndexPage
featured_section_1_title = models.CharField(
null=True,
blank=True,
max_length=255,
help_text='Title to display above the promo copy'
)
featured_section_1 = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='First featured section for the homepage. Will display up to '
'three child items.',
verbose_name='Featured section 1'
)
featured_section_2_title = models.CharField(
null=True,
blank=True,
max_length=255,
help_text='Title to display above the promo copy'
)
featured_section_2 = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Second featured section for the homepage. Will display up to '
'three child items.',
verbose_name='Featured section 2'
)
featured_section_3_title = models.CharField(
null=True,
blank=True,
max_length=255,
help_text='Title to display above the promo copy'
)
featured_section_3 = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text='Third featured section for the homepage. Will display up to '
'six child items.',
verbose_name='Featured section 3'
)
content_panels = Page.content_panels + [
MultiFieldPanel([
ImageChooserPanel('image'),
FieldPanel('hero_text', classname="full"),
MultiFieldPanel([
FieldPanel('hero_cta'),
PageChooserPanel('hero_cta_link'),
]),
], heading="Hero section"),
MultiFieldPanel([
ImageChooserPanel('promo_image'),
FieldPanel('promo_title'),
FieldPanel('promo_text'),
], heading="Promo section"),
StreamFieldPanel('body'),
MultiFieldPanel([
MultiFieldPanel([
FieldPanel('featured_section_1_title'),
PageChooserPanel('featured_section_1'),
]),
MultiFieldPanel([
FieldPanel('featured_section_2_title'),
PageChooserPanel('featured_section_2'),
]),
MultiFieldPanel([
FieldPanel('featured_section_3_title'),
PageChooserPanel('featured_section_3'),
]),
], heading="Featured homepage sections", classname="collapsible")
]
def __str__(self):
return self.title
class FormField(AbstractFormField):
logging.warning('FormField')
page = ParentalKey('FormPage', related_name='form_fields', on_delete=models.CASCADE)
field_type = models.CharField(
verbose_name='field type',
max_length=16,
choices=FORM_FIELD_CHOICES + (('fileupload', 'File Upload'),)
)
class CustomFormBuilder(FormBuilder):
logging.warning('CustomFormBuilder')
def create_fileupload_field(self, field, options):
return forms.FileField(**options)
class CustomSubmissionsListView(SubmissionsListView):
"""
further customisation of submission list can be done here
"""
logging.warning('CustomSubmissionsListView')
pass
class CustomFormSubmission(AbstractFormSubmission):
# important - adding this custom model will make existing submissions unavailable
# can be resolved with a custom migration
def get_data(self):
"""
Here we hook in to the data representation that the form submission returns
Note: there is another way to do this with a custom SubmissionsListView
However, this gives a bit more granular control
"""
logging.warning('CustomFormSubmission')
file_form_fields = [
field.clean_name for field in self.page.specific.get_form_fields()
if field.field_type == 'fileupload'
]
data = super().get_data()
for field_name, field_vale in data.items():
if field_name in file_form_fields:
# now we can update the 'representation' of this value
# we could query the FormUploadedFile based on field_vale (pk)
# then return the filename etc.
pass
return data
def upload_validator(value):
raise ValidationError(value)
class FormUploadedFile(models.Model):
logging.warning('FormUploadedFile')
file = models.FileField(upload_to="files/%Y/%m/%d", validators=[upload_validator])
# file = models.FileField(upload_to="files/%Y/%m/%d")
field_name = models.CharField(blank=True, max_length=254)
class FormPage(AbstractEmailForm):
form_builder = CustomFormBuilder
submissions_list_view_class = CustomSubmissionsListView
logging.warning('FormPage')
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
# body = RichTextField(default="")
# body = StreamField(BaseStreamBlock())
body = StreamField(
BaseStreamBlock(), verbose_name="Page body", blank=True
)
thank_you_text = RichTextField(blank=True)
# Note how we include the FormField object via an InlinePanel using the
# related_name value
content_panels = AbstractEmailForm.content_panels + [
ImageChooserPanel('image'),
StreamFieldPanel('body'),
InlinePanel('form_fields', label="Form fields"),
FieldPanel('thank_you_text', classname="full"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "Email"),
]
def get_submission_class(self):
"""
Returns submission class.
Important: will make your existing data no longer visible, only needed if you want to customise
the get_data call on the form submission class, but might come in handy if you do it early
You can override this method to provide custom submission class.
Your class must be inherited from AbstractFormSubmission.
"""
# print('get_submission_class')
return CustomFormSubmission
def process_form_submission(self, form):
"""
Accepts form instance with submitted data, user and page.
Creates submission instance.
You can override this method if you want to have custom creation logic.
For example, if you want to save reference to a user.
"""
# print('process_form_submission')
file_form_fields = [field.clean_name for field in self.get_form_fields() if field.field_type == 'fileupload']
for (field_name, field_value) in form.cleaned_data.items():
if field_name in file_form_fields:
uploaded_file = FormUploadedFile.objects.create(
file=field_value,
field_name=field_name
)
# store a reference to the pk (as this can be converted to JSON)
form.cleaned_data[field_name] = uploaded_file.pk
return self.get_submission_class().objects.create(
form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder),
page=self,
)

How to make the ImageField optional in django rest framework API

I'm pretty tired finding solution to make the imagefield optional in django rest framework API. I tried each and every solution I found in stack overflow but nothing seems to be working fine. I know there are fewer posts related to same query but I didn't find the solution in that.
Let me explain you what is my requirements.
Here is my User model with some basic info fields including an Image field.
models.py
class User(models.Model):
firstname = models.CharField(max_length=100, validators=[firstname_check])
lastname = models.CharField(max_length=100, blank=True, null=True, validators=[lastname_check])
username = models.CharField(max_length=100, unique=True, blank=True, null=True, validators=[username_check])
email = models.EmailField(max_length=100, unique=True)
password = models.CharField(max_length=50, blank=True, null=True, validators=[password_check])
mobnum = models.CharField(max_length=50, blank=True, null=True, validators=[mobile_num_len])
timestamp = models.DateTimeField(auto_now=True)
gender = models.CharField(max_length=50, blank=True, null=True)
language = models.CharField(max_length=100, blank=True, null=True)
profile_img = models.ImageField(upload_to ='uploads/', blank=True, null=True)
is_active = models.BooleanField(default=False, blank=True, null=True)
serializer.py
from utils import Base64ImageField
class UserMannualSerializer(serializers.ModelSerializer):
profile_img = Base64ImageField(
max_length=None, use_url=True, allow_empty_file=True, required=False
)
class Meta:
model = User
fields = [
'firstname',
'lastname',
'username',
'email',
'password',
'mobnum',
'gender',
'language',
'timestamp'
'profile_img'
]
Here is the Base64ImageField() function which I've written inside the utils.py file. Which will take the base64 string and convert it back and store in the server and that is happening properly. But when I don't upload the image, it throws an error. If I pass the (allow_empty_file=True, required=False) attributes and its values in the Base64ImageField(), even also it doesn't work at all.
utils.py
class Base64ImageField(serializers.ImageField):
"""
A Django REST framework field for handling image-uploads through raw post data.
It uses base64 for encoding and decoding the contents of the file.
Heavily based on
https://github.com/tomchristie/django-rest-framework/pull/1268
Updated for Django REST framework 3.
"""
def to_internal_value(self, data):
# Check if this is a base64 string
if isinstance(data, six.string_types):
# Check if the base64 string is in the "data:" format
if 'data:' in data and ';base64,' in data:
# Break out the header from the base64 content
header, data = data.split(';base64,')
# Try to decode the file. Return validation error if it fails.
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
# Generate file name:
file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough.
# Get the file name extension:
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension, )
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
Note: If I give default field in the ImageField, then also it's not working. It's not taking the default image if I don't upload one.
I'm working with Python 3.6, Django 3.1.1, djangorestframework 3.11.1
Please let me know if someone has already been faced this problem and got the solution or any alternative would also help. I would appreciate your help. Thank you.
Edited part
Here is the code from another app, how I'm sending the code from the other django app.
def register_confirm(request):
"""
getting the sign-up required values
dumping the all the parameters as json
using requests library to achieve the register via API
passing the success response to profile.html
"""
std_mobnum = ''
if request.method == "POST":
firstname = request.POST.get("fname")
lastname = request.POST.get("lname")
username = request.POST.get("uname")
email = request.POST.get("email")
password = request.POST.get("pswd")
mobnum = request.POST.get("mobnum")
image = request.POST.get('imagtobase64')
gender = request.POST.get("gender")
language = request.POST.get("language")
params={
'firstname':firstname,
'lastname':lastname,
'username':username,
'email':email,
'password':password,
'mobnum':mobnum,
'profile_img':image,
'gender':gender,
'language':language,
}
print(params)
headers = {'content-type':'application/json'}
response = requests.post("http://127.0.0.1:8000/user-create/", data=json.dumps(params), headers=headers)
user_data = response.json()
print(user_data)
return JsonResponse(user_data)
Note: I'm sending the base64 string of an image using jquery to Api.
This is the code of views.py file where I'm handling the serializer and post request.
views.py
api_view(['POST'])
#parser_classes([MultipartJsonParser, FormParser, JSONParser])
def userCreate(request):
status=''
message = ''
data ={}
try:
mannualSerializer = UserMannualSerializer(data = request.data)
print("serializer started")
if mannualSerializer.is_valid():
print("form validated")
mannualSerializer.save()
print("saved")
status='success'
message = 'User created successfully.'
data= mannualSerializer.data
except Exception as error:
status='failure'
message = error
response = {
"status":status,
"message":message,
"data":data
}
return Response(response)

Django multi language model / filter post by languages

There is my simple blog model;
class Article(models.Model):
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title_en = models.CharField(max_length = 120, verbose_name="Title_En")
title_de = models.CharField(max_length = 120, verbose_name="Title_De")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content_en = RichTextField(verbose_name="Content_En")
content_de = RichTextField(verbose_name="Content_De")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
def __str__(self):
return self.title
I use url's with language like this;
domainname.com/en/
domainname.com/de/
For example, how can I show only the contents that belong to title_de and content_de in the domainname.com/de urls?
How can I do filtering with language? Is there an easy solution to this?
(I usage django 2.1.2. i try django-modeltranslation or others dont work this django version...)
Thanks...
You can create a descriptor class that wraps the translated fields e.g.,
from django.utils import translation
class TranslatedField:
def __init__(self, field_name):
self.partial_field_name = field_name
def __get__(self, obj, objtype):
return getattr(obj, self.field_name)
def __set__(self, obj, value):
return setattr(obj, self.field_name, value)
#property
def field_name(self):
language_code = translation.get_language()
rerurn self.partial_field_name + '_' + language_code
class Article(models.Model):
title_en = models.CharField(max_length=120)
title_de = models.CharField(max_length=120)
title = Translated Field('title')
Then you can do
article = Article.objects.create(
title_en='In english',
title_de='In German'
)
print(article.title) # 'In english'
translation.set_language('de')
print(article.title) # 'In German'
article.title = 'In German!'
print(article.title) # 'In German!'
translation.set_language('en')
print(article.title) # 'In english'
(Untested, so there may be typos)
I would use something out of the box like https://github.com/deschler/django-modeltranslation
Filtering based on keyward argument is one of the option for this problem.I would prefer to add a language field 'EN' or 'DE' rather than repeating same kind of title and content field and filtering based on that. For example,
Article Model can be like
class Article(models.Model):
LANGUAGE_TYPES = (
('EN', 'EN'),
('DE', 'DE'),
)
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title = models.CharField(max_length = 120, verbose_name="Title")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content = RichTextField(verbose_name="Content")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
language = models.CharField(
max_length=10, choices=LANGUAGE_TYPES)
def __str__(self):
return self.title
Our urls can be like
from django.urls import path
from .views import (ArticleView)
urlpatterns = [
path('article/<slug:type>/', ArticleView.as_view(), name='article'),
]
And Our view can be like
from rest_framework import views, status
from .serializers import ArticleSerializer
from .models import Article
class ArticleView(views.APIView):
def get(self, request):
article_language_type = self.kwargs.get('type', None)
articles = Article.objects.filter(language=article_language_type)
serializer = ArticleSerializer(articles, many=True)
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Django CMS Plugin Development: __init__() got an unexpected keyword argument 'instance'

I am in process of creating Django CMS plugin. So far I have created a form, model and plugin file. I want to save SETTINGS info, but when I go to save it it gives error as mentioned above. Below are details.
cms_plugins.py
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from cms.models import CMSPlugin
from src.survey.forms import SurveyForm
from . import models
class SurveyPluginPublisher(CMSPluginBase):
"""Show Polls entered by Admin."""
cache = False
form = SurveyForm
model = models.SurveyPluginModel # Must add to show stuff.
module = "Survey"
name = "Awesome Survey v1.0"
render_template = 'survey/_hello.html'
plugin_pool.register_plugin(SurveyPluginPublisher)
forms.py
from django import forms
from src.survey.models import Survey
models.py
class SurveyForm(forms.Form):
# all_surveys = Survey.objects.only('name')
# surveys = forms.ModelChoiceField(queryset=all_surveys)
headline = forms.CharField(max_length=255, help_text='Enter Headline')
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 5, 'cols': 100}),help_text='Enter Description')
models.py
class SurveyPluginModel(CMSPlugin):
name = models.CharField("Survey Name", max_length=255, default='Survey Name',
help_text='Enter Survey Name')
description = models.CharField("Survey Description", max_length=500, blank=True, help_text='Write Description here')
def __str__(self):
return "Returning some Survey Text"
SurveyForm should be a subclass of ModelForm
class SurveyForm(forms.ModelForm):
class Meta:
model = SurveyPluginModel

Extending django-registration using signals

I have found here on stackoverflow a solution to extend django-registration with new fields using signals. Here's the link : http://dmitko.ru/?p=546 .
I have created extended profile model, extended form, added required options to settings , defined urls and the proper form is displayed but only normal User (from auth module) is created. Why is that happening ?
account.models :
from django.db import models
from django.contrib.auth.models import User
from registration.signals import user_registered
import hashlib
class InheritedProfile(models.Model):
first_name = models.CharField("Name", max_length=50, blank=True, null=True)
last_name = models.CharField("Last name", max_length=50, blank=True, null=True)
pid = models.CharField("PESEL", max_length=11, blank=True, null=True)
street = models.CharField("Street", max_length=50, blank=True, null=True)
number = models.CharField("Flat/house number", max_length=10, blank=True, null=True)
code = models.CharField("Zip ", max_length=6, blank=True, null=True)
city = models.CharField("City", max_length=50, blank=True, null=True)
class Meta:
abstract=True
class UserProfile(InheritedProfile, User):
def upload_path(self, field_attname):
filename = hashlib.md5(field_attname).hexdigest()[:4] + "_" + field_attname
return "uploads/users/%s" % (filename,)
image = models.ImageField(upload_to=upload_path, verbose_name="Image", blank=True, null=True)
def user_created(sender, user, request, **kwargs):
form = ExtendedRegistrationForm(request.POST)
extended_user = UserProfile(user=user)
extended_user.is_active = False
extended_user.first_name = form.extended_user['first_name']
extended_user.last_name = form.extended_user['last_name']
extended_user.pid = form.extended_user['pid']
extended_user.image = form.extended_user['image']
extended_user.street = form.extended_user['street']
extended_user.number = form.extended_user['number']
extended_user.code = form.extended_user['code']
extended_user.city = form.extended_user['city']
extended_user.save()
user_registered.connect(user_created)
I need this InheritedProfile to be abstract as other models will use the same fields.
account.forms
from django import forms
#import strings
from registration.forms import RegistrationForm
from models import UserProfile, InheritedProfile
class ExtendedRegistrationForm(RegistrationForm):
first_name = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="First name")
last_name = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="Last name")
pid = forms.RegexField(regex=r'^\d{11}', max_length=11 ,widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)))
image = forms.ImageField(label="Image",)
street = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="Street")
number = forms.CharField(widget=forms.TextInput, label="House/flat number")
code = forms.RegexField(regex=r'^\d{2}[-]\d{3}', max_length=6, widget=forms.TextInput(attrs=attrs_dict), label="Postal code")
city = forms.CharField(widget=forms.TextInput, label="City")
and options added to settings :
AUTH_PROFILE_MODULE = 'account.UserProfile'
ACCOUNT_ACTIVATION_DAYS = 7
finally this is how the registration signal looks like :
from django.dispatch import Signal
# A new user has registered.
user_registered = Signal(providing_args=["user", "request"])
EDIT:
Indentation of user_created changes nothing until I've tried changing
user_registered.connect(user_created)
to
user_registered.connect(user_created, sender=UserProfile)
Now I was getting :
"SMTPServerDisconnected
Exception Location: /bin/python-2.6.1/lib/python2.6/smtplib.py in getreply, line 340 "
Traceback:
File "/home/fandrive/site-packages/django/core/handlers/base.py" in get_response
92. response = callback(request, *callback_args, **callback_kwargs)
File "/home/fandrive/registration/views.py" in register
47. new_user = backend.register(request, **form.cleaned_data)
File "/home/fandrive/registration/backends/default/__init__.py" in register
20. password, site)
File "/home/fandrive/site-packages/django/db/transaction.py" in _commit_on_success
240. res = func(*args, **kw)
File "/home/fandrive/registration/models.py" in create_inactive_user
80. registration_profile.send_activation_email(site)
File "/home/fandrive/registration/models.py" in send_activation_email
256. self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
File "/home/fandrive/site-packages/django/contrib/auth/models.py" in email_user
271. send_mail(subject, message, from_email, [self.email])
File "/home/fandrive/site-packages/django/core/mail.py" in send_mail
390. connection=connection).send()
File "/home/fandrive/site-packages/django/core/mail.py" in send
266. return self.get_connection(fail_silently).send_messages([self])
File "/home/fandrive/site-packages/django/core/mail.py" in send_messages
172. sent = self._send(message)
File "/home/fandrive/site-packages/django/core/mail.py" in _send
186. email_message.message().as_string())
File "/bin/python-2.6.1/lib/python2.6/smtplib.py" in sendmail
708. self.rset()
File "/bin/python-2.6.1/lib/python2.6/smtplib.py" in rset
438. return self.docmd("rset")
File "/bin/python-2.6.1/lib/python2.6/smtplib.py" in docmd
363. return self.getreply()
File "/bin/python-2.6.1/lib/python2.6/smtplib.py" in getreply
340. raise SMTPServerDisconnected("Connection unexpectedly closed")
Exception Type: SMTPServerDisconnected at /user/register/
Exception Value: Connection unexpectedly closed
Even though I'm using dummy email backend at the moment. Commenting out sending mail function upon registration solved this problem but still my extended user is not created.
May be the problem is in the way how you connect to the signal? In my solution in was:
def user_created(sender, user, request, **kwargs):
form = UserRegistrationForm(request.POST)
data = profile.Profile(user=user)
data.city_id = form.data["city"]
data.save()
from registration.signals import user_registered
user_registered.connect(user_created)
and in yours:
from django.dispatch import Signal
# A new user has registered.
user_registered = Signal(providing_args=["user", "request"])
Also, I would switch on logging to ensure that your method is called. My solution works fine in the production, if you need I can look for other details.