Edit a foreign key's fields in a template - html

I'm unsure of how I can edit a foreign key's fields from the template of another model - my changes do not update the model on post, see below.
models
class Projects(models.Model):
name = models.CharField(max_length=250)
class Current(models.Model):
fk_user = models.OneToOneField(User, on_delete=models.CASCADE)
fk_project = models.ForeignKey(projects, default='1', on_delete=models.CASCADE)
views
class current(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Current
fields = [
'fk_project'
]
template_name = 'users/current.html'
context_object_name = 'current'
def form_valid(self, form):
form.instance.fk_user = self.request.user
form.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)
def test_func(self):
post = self.get_object()
if self.request.user == post.fk_user:
return True
return False
current.html
<form method="POST">{% csrf_token %}
<input type="text" value="{{ current.fk_project.name }}"/>
<button type="submit">post</button>

I would suggest you amend form_valid to follow the usual conventions. No idea if this will fix it, but
def form_valid(self, form):
instance = form.save( commit=False)
instance.fk_user = self.request.user
instance.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)
If the desire is to update fields in the Project which is identified by the form as well as to store it in the Current instance which is the subject of this UpdateView, you can accomplish this by adding appropriate non-Model fields to the form and then processing the data in form_valid. For example, to pick a project, link it as at present, and simultaneously update its name:
class CurrentAndProjectUpdateForm( forms.ModelForm):
class Meta:
model = Current
fields = ['fk_project', ]
name = forms.CharField( ...) # new name for the related project
class current(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Current # don't think this now necessary
form_class = CurrentAndProjectUpdateForm # because it's implicit in this form
# fields = # DEFINITELY not needed here now
...
def form_valid(self, form):
instance = form.save( commit=False)
instance.fk_user = self.request.user
instance.save()
project = instance.fk_project
project.name = form.cleaned_data['name']
project.save()
# return super().form_valid(form)
return HttpResponseRedirect(self.request.path_info)

Related

Print parent column name

Given the following codes:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_ECHO"] = True
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"
db = SQLAlchemy(app)
class File(db.Model):
__tablename__ = "file"
_id = db.Column(db.String, primary_key=True)
file_name = db.Column(db.String)
category_id = db.Column(db.String, db.ForeignKey("category._id"))
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
class Category(db.Model):
__tablename__ = "category"
_id = db.Column(db.String, primary_key=True)
name = db.Column(db.String)
files = db.relationship("File", backref="category")
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
db.drop_all()
db.create_all()
categories0 = Category(_id="aca50a26-5d3f-4c4d-872b-83b663d5304f",name="Apple")
files0 = File(_id="8a95ba11-e2aa-407d-bac9-609e6c559731",file_name="8a95ba11-e2aa-407d-bac9-609e6c559731.jpg",category=categories0)
db.session.add_all([categories0,files0])
db.session.commit()
results=db.session.query(File).join(Category, File.category).filter(Category._id=="aca50a26-5d3f-4c4d-872b-83b663d5304f").all()
#app.route('/print')
def printMsg():
return jsonify([c.as_dict() for c in results])
if __name__ == '__main__':
app.run(debug=True)
When I call the endpoint /print, it returns
[
{
"_id": "8a95ba11-e2aa-407d-bac9-609e6c559731",
"category_id": "aca50a26-5d3f-4c4d-872b-83b663d5304f",
"file_name": "8a95ba11-e2aa-407d-bac9-609e6c559731.jpg"
}
]
But I need the category_name as output
[
{
"_id": "8a95ba11-e2aa-407d-bac9-609e6c559731",
"category_id": "aca50a26-5d3f-4c4d-872b-83b663d5304f",
"category_name": "Apple",
"file_name": "8a95ba11-e2aa-407d-bac9-609e6c559731.jpg"
}
]
How should I achieve that?
Here's code working as you requested. There is some other help I'll offer in here...
You code iterated columns. That's the Database's view of things. You should iterate the attributes of the model. In many cases, they're the same for fields (not always). But for relationships, you HAVE to be looking at the model.
Instead of pulling the related attribute into the dictionary of fields, I offer a 2nd solution to NEST the related fields in the response. As your models grow and scale, you will appreciate this more than you may now.
I changed some formatting for PEP8. Even for SO posts, it helps readability and its always good practice.
I would STRONGLY consider you DO NOT use either of these solutions. Serialization is a tricky process. For simple fields/relationships like your example, it may not seem that way. Again, as you scale, you will be handling a lot of edge cases and writing code that's been written and tested many time before. Consider using Marshmallow / Flask-Marshmallow. It's a great library that makes serialization and relationship nesting trivial.
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
import sqlalchemy as sa
app = Flask(__name__)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
db = SQLAlchemy(app)
class File(db.Model):
__tablename__ = 'file'
_id = db.Column(
db.String,
primary_key=True,
)
file_name = db.Column(db.String, )
category_id = db.Column(
db.String,
db.ForeignKey('category._id'),
)
def as_dict(self):
"""Return serialzed attributes + related category name"""
serialized = {
attr.key: getattr(self, attr.key)
for attr in sa.orm.class_mapper(self.__class__).iterate_properties
if isinstance(attr, sa.orm.properties.ColumnProperty)
}
serialized['category_name'] = self.category.name
return serialized
def as_dict_with_relationships(self):
"""Return serialzed attributes + nested relationships"""
serialized = {}
for attr in sa.orm.class_mapper(self.__class__).iterate_properties:
if isinstance(attr, sa.orm.properties.ColumnProperty):
serialized[attr.key] = getattr(self, attr.key)
elif isinstance(attr, sa.orm.relationships.RelationshipProperty):
serialized[attr.key] = getattr(self, attr.key).as_dict()
else:
print(f'not handling {attr}, {type(attr)}')
return serialized
class Category(db.Model):
__tablename__ = 'category'
_id = db.Column(
db.String,
primary_key=True,
)
name = db.Column(db.String, )
files = db.relationship(
'File',
backref='category',
)
def as_dict(self):
return {
c.name: getattr(self, c.name)
for c in self.__table__.columns
}
db.drop_all()
db.create_all()
categories0 = Category(
_id='aca50a26-5d3f-4c4d-872b-83b663d5304f',
name='Apple',
)
files0 = File(
_id='8a95ba11-e2aa-407d-bac9-609e6c559731',
file_name='8a95ba11-e2aa-407d-bac9-609e6c559731.jpg',
category=categories0,
)
db.session.add_all([categories0, files0])
db.session.commit()
#app.route('/print')
def print_msg():
"""Return serialized results- top level attributes + explicit other data"""
results = db.session.query(File) \
.join(Category, File.category) \
.filter(
Category._id == 'aca50a26-5d3f-4c4d-872b-83b663d5304f',
) \
.all()
return jsonify([
record.as_dict()
for record in results
])
def print_msg2():
"""Return serialized results- top level attributes + nested relationships"""
results = db.session.query(File) \
.join(Category, File.category) \
.filter(
Category._id == 'aca50a26-5d3f-4c4d-872b-83b663d5304f',
) \
.all()
return jsonify([
record.as_dict_with_relationships()
for record in results
])
if __name__ == '__main__':
app.run(debug=True)

How to make Django views require a user login?

I have a Django project with a login screen; when I enter my username and password I get directed to the home page. This works fine! But nothing is secure, I can just go to the view at /allstudyplans without logging in and see all the information there. My Question is how do I make it so it's not possible to go /allstudyplans without logging in first?
Form
User = get_user_model()
class UserLoginForm(forms.Form):
username = forms.CharField(label='', widget=forms.TextInput(
attrs={
'class': 'form-control',
'autofocus': '',
'placeholder': 'Användarnamn',
}
), )
password = forms.CharField(label='', widget=forms.PasswordInput(
attrs={
'class': 'form-control mt-1',
'placeholder': 'Lösenord',
}
), )
# https://docs.djangoproject.com/en/2.1/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
def clean(self):
cleaned_data = super().clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
if username and password:
user = authenticate(username=username, password=password)
if not user:
raise forms.ValidationError(
'Oh! I can\'t find that user - create user first!')
elif not user.check_password(password):
raise forms.ValidationError(
'Oh! That password is incorrect - try again!')
elif not user.is_active:
raise forms.ValidationError(
'Oh! That user is not active in the database!')
Views
def home(request):
next = (request.GET.get('next'))
form = UserLoginForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
login(request, user)
if next:
return redirect(next)
return redirect('/')
context = {
'form': form,
}
return render(request, "utility-login.html", context)
def Assignments(request):
return render(request, 'nav-side-team.html')
def DetailAssignment(request):
obj = Assignment.objects.all()
context = {
'object': obj
}
return render(request, 'nav-side-project.html', context)
def studyplans(request):
return render(request, 'allStudyplans.html')
def detailStudyplan(request):
return render(request, 'detailStudyplan.html')
Also a Home View in the normal project file (not the app)
#login_required
def homee(request):
return render(request, "allstudyplans.html", {})
Urls in the project:
urlpatterns = [
path('admin/', admin.site.urls),
path('account/', include('accounts.urls')),
path('', include('accounts.urls'))
]
Urls in the app:
urlpatterns = [
path('', views.studyplans),
path('login', views.home),
path('nav-side-team.html', views.Assignments),
path('nav-side-project.html', views.DetailAssignment),
path('allstudyplans.html', views.studyplans),
path('detailStudyplan.html', views.detailStudyplan),
]
Tried this:
#login_required
def Assignments(request):
if not request.user.is_authenticated:
return redirect('%s?next=%s' % ('utility-login.html', request.path))
return render(request, 'nav-side-team.html')
studyplans is missing the decorator login_required.
According to the docs, https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator, the easiest way should be
#login_required
def studyplans(request):
return render(request, 'allStudyplans.html')
Another way to do it – probably equivalent to the above – is with "mixins":
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
class MyView(LoginRequiredMixin, View):
...
Notice that "mixin" class names should occur first in the list, before View.
(Notice that I also show that there's a "permission-required mixin.")
I guess it just comes down to personal preference, and consistency with the existing source-code of the application.

ID collision with Graphene-SQLAlchemy Interface class plus Node Interface

I've written Graphene models for polymorphic entities represented in my database w/SQLalchemy.
The problem is simple:
I want to create an interface that reflects my SQLAlchemy models for Graphene but also either a) implements Node or b) does not conflict with Node and allows me to retrieve the model's ID without needing to add ... on Node {id} to the query string.
have to exclude the ID field from my ORM-based interface or field conflicts with the Node interface, by doing so in order to get the ID then you need to add ...on Node { id }, which is ugly.
I created an SQLAlchemyInterface object that extends graphene.Interface. Many (but not all) of my models used this as well as Node as interfaces. The first problem was that this contains an ID field and it conflicted with the Node interface.
I excluded the id field to not interfere with Node, but then found I could not directly query ID on my models anymore, and had to add ... on Node {id} to the query string.
I then decided to have this SQLAlchemyInterface extend Node. I don't love this approach because I need to use another (named) Node interface for all of my models that don't necessarily need to implement SQLAlchemyInterface
class SQLAlchemyInterface(Node):
#classmethod
def __init_subclass_with_meta__(
cls,
model=None,
registry=None,
only_fields=(),
exclude_fields=(),
connection_field_factory=default_connection_field_factory,
_meta=None,
**options
):
_meta = SQLAlchemyInterfaceOptions(cls)
_meta.name = f'{cls.__name__}Node'
autoexclude_columns = exclude_autogenerated_sqla_columns(model=model)
exclude_fields += autoexclude_columns
assert is_mapped_class(model), (
"You need to pass a valid SQLAlchemy Model in " '{}.Meta, received "{}".'
).format(cls.__name__, model)
if not registry:
registry = get_global_registry()
assert isinstance(registry, Registry), (
"The attribute registry in {} needs to be an instance of "
'Registry, received "{}".'
).format(cls.__name__, registry)
sqla_fields = yank_fields_from_attrs(
construct_fields(
model=model,
registry=registry,
only_fields=only_fields,
exclude_fields=exclude_fields,
connection_field_factory=connection_field_factory
),
_as=Field
)
if not _meta:
_meta = SQLAlchemyInterfaceOptions(cls)
_meta.model = model
_meta.registry = registry
connection = Connection.create_type(
"{}Connection".format(cls.__name__), node=cls)
assert issubclass(connection, Connection), (
"The connection must be a Connection. Received {}"
).format(connection.__name__)
_meta.connection = connection
if _meta.fields:
_meta.fields.update(sqla_fields)
else:
_meta.fields = sqla_fields
super(SQLAlchemyInterface, cls).__init_subclass_with_meta__(_meta=_meta, **options)
#classmethod
def Field(cls, *args, **kwargs): # noqa: N802
return NodeField(cls, *args, **kwargs)
#classmethod
def node_resolver(cls, only_type, root, info, id):
return cls.get_node_from_global_id(info, id, only_type=only_type)
#classmethod
def get_node_from_global_id(cls, info, global_id, only_type=None):
try:
node: DeclarativeMeta = one_or_none(session=info.context.get('session'), model=cls._meta.model, id=global_id)
return node
except Exception:
return None
#staticmethod
def from_global_id(global_id):
return global_id
#staticmethod
def to_global_id(type, id):
return id
Interface impls, Models + Query code examples:
class CustomNode(Node):
class Meta:
name = 'UuidNode'
#staticmethod
def to_global_id(type, id):
return '{}:{}'.format(type, id)
#staticmethod
def get_node_from_global_id(info, global_id, only_type=None):
type, id = global_id.split(':')
if only_type:
# We assure that the node type that we want to retrieve
# is the same that was indicated in the field type
assert type == only_type._meta.name, 'Received not compatible node.'
if type == 'User':
return one_or_none(session=info.context.get('session'), model=User, id=global_id)
elif type == 'Well':
return one_or_none(session=info.context.get('session'), model=Well, id=global_id)
class ControlledVocabulary(SQLAlchemyInterface):
class Meta:
name = 'ControlledVocabularyNode'
model = BaseControlledVocabulary
class TrackedEntity(SQLAlchemyInterface):
class Meta:
name = 'TrackedEntityNode'
model = TrackedEntityModel
class Request(SQLAlchemyObjectType):
"""Request node."""
class Meta:
model = RequestModel
interfaces = (TrackedEntity,)
class User(SQLAlchemyObjectType):
"""User Node"""
class Meta:
model = UserModel
interfaces = (CustomNode,)
class CvFormFieldValueType(SQLAlchemyObjectType):
class Meta:
model = CvFormFieldValueTypeModel
interfaces = (ControlledVocabulary,)
common_field_kwargs = {'id': graphene.UUID(required=False), 'label': graphene.String(required=False)}
class Query(graphene.ObjectType):
"""Query objects for GraphQL API."""
node = CustomNode.Field()
te_node = TrackedEntity.Field()
cv_node = ControlledVocabulary.Field()
# Non-Tracked Entities:
users: List[User] = SQLAlchemyConnectionField(User)
# Generic Query for any Tracked Entity:
tracked_entities: List[TrackedEntity] = FilteredConnectionField(TrackedEntity, sort=None, filter=graphene.Argument(TrackedEntityInput))
# Generic Query for any Controlled Vocabulary:
cv: ControlledVocabulary = graphene.Field(ControlledVocabulary, controlled_vocabulary_type_id=graphene.UUID(required=False),
base_entry_key=graphene.String(required=False),
**common_field_kwargs)
cvs: List[ControlledVocabulary] = FilteredConnectionField(ControlledVocabulary, sort=None, filter=graphene.Argument(CvInput))
#staticmethod
def resolve_with_filters(info: ResolveInfo, model: Type[SQLAlchemyObjectType], **kwargs):
query = model.get_query(info)
log.debug(kwargs)
for filter_name, filter_value in kwargs.items():
model_filter_column = getattr(model._meta.model, filter_name, None)
log.debug(type(filter_value))
if not model_filter_column:
continue
if isinstance(filter_value, SQLAlchemyInputObjectType):
log.debug(True)
filter_model = filter_value.sqla_model
q = FilteredConnectionField.get_query(filter_model, info, sort=None, **kwargs)
# noinspection PyArgumentList
query = query.filter(model_filter_column == q.filter_by(**filter_value))
log.info(query)
else:
query = query.filter(model_filter_column == filter_value)
return query
def resolve_tracked_entity(self, info: ResolveInfo, **kwargs):
entity: TrackedEntity = Query.resolve_with_filters(info=info, model=BaseTrackedEntity, **kwargs).one()
return entity
def resolve_tracked_entities(self, info, **kwargs):
query = Query.resolve_with_filters(info=info, model=BaseTrackedEntity, **kwargs)
tes: List[BaseTrackedEntity] = query.all()
return tes
def resolve_cv(self, info, **kwargs):
cv: List[BaseControlledVocabulary] = Query.resolve_with_filters(info=info, model=BaseControlledVocabulary, **kwargs).one()
log.info(cv)
return cv
def resolve_cvs(self, info, **kwargs):
cv: List[BaseControlledVocabulary] = Query.resolve_with_filters(info=info, model=BaseControlledVocabulary, **kwargs).all()
return cv
schema:
schema = Schema(query=Query, types=[*tracked_members, *cv_members])
I would like to be able to not extend Node with SQLAlchemyInterface and rather add Node back to the list of interfaces for TrackedEntity and ControlledVocabulary but be able to perform a query like this:
query queryTracked {
trackedEntities{
id
(other fields)
... on Request {
(request specific fields)
}
}

SQLAlchemy session voes in unittest

I've just started using SQLAlchemy a few days ago and right now I'm stuck with a problem that I hope anyone can shed some light on before I loose all my hair.
When I run a unittest, see snippet below, only the first test in the sequence is passing. The test testPhysicalPrint works just fine, but testRecordingItem fails with NoResultFound exception - No row was found for one(). But if I remove testPhysicalPrint from the test class, then testRecordingItem works.
I assume that the problem has something to do with the session, but I can't really get a grip of it.
In case anyone wonders, the setup is as follows:
Python 3.1 (Ubuntu 10.04 package)
SQLAlchemy 0.7.2 (easy_install:ed)
PostgreSQL 8.4.8 (Ubuntu 10.04 package)
PsycoPG2 2.4.2 (easy_installed:ed)
Exemple test:
class TestSchema(unittest.TestCase):
test_items = [
# Some parent class products
PrintItem(key='p1', title='Possession', dimension='30x24'),
PrintItem(key='p2', title='Andrzej Żuławski - a director', dimension='22x14'),
DigitalItem(key='d1', title='Every Man His Own University', url='http://www.gutenberg.org/files/36955/36955-h/36955-h.htm'),
DigitalItem(key='d2', title='City Ballads', url='http://www.gutenberg.org/files/36954/36954-h/36954-h.htm'),
]
def testPrintItem(self):
item = self.session.query(PrintItem).filter(PrintItem.key == 'p1').one()
assert item.title == 'Possession', 'Title mismatch'
def testDigitalItem(self):
item2 = self.session.query(DigitalItem).filter(DigitalItem.key == 'd2').one()
assert item2.title == 'City Ballads', 'Title mismatch'
def setUp(self):
Base.metadata.create_all()
self.session = DBSession()
self.session.add_all(self.test_items)
self.session.commit()
def tearDown(self):
self.session.close()
Base.metadata.drop_all()
if __name__ == '__main__':
unittest.main()
UPDATE
Here is the working code snippet.
# -*- coding: utf-8 -*-
import time
import unittest
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import *
Base = declarative_base()
engine = create_engine('sqlite:///testdb', echo=False)
DBSession = sessionmaker(bind=engine)
class ItemMixin(object):
"""
Commons attributes for items, ie books, DVD:s...
"""
__tablename__ = 'testitems'
__table_args__ = {'extend_existing':True}
id = Column(Integer, autoincrement=True, primary_key=True)
key = Column(Unicode(16), unique=True, nullable=False)
title = Column(UnicodeText, default=None)
item_type = Column(Unicode(20), default=None)
__mapper_args__ = {'polymorphic_on': item_type}
def __init__(self, key, title=None):
self.key = key
self.title = title
class FooItem(Base, ItemMixin):
foo = Column(UnicodeText, default=None)
__mapper_args__ = {'polymorphic_identity':'foo'}
def __init__(self, foo=None, **kwargs):
ItemMixin.__init__(self, **kwargs)
self.foo = foo
class BarItem(Base, ItemMixin):
bar = Column(UnicodeText, default=None)
__mapper_args__ = {'polymorphic_identity':'bar'}
def __init__(self, bar=None, **kwargs):
ItemMixin.__init__(self, **kwargs)
self.bar = bar
# Tests
class TestSchema(unittest.TestCase):
# Class variables
is_setup = False
session = None
metadata = None
test_items = [
FooItem(key='f1', title='Possession', foo='Hello'),
FooItem(key='f2', title='Andrzej Żuławsk', foo='World'),
BarItem(key='b1', title='Wikipedia', bar='World'),
BarItem(key='b2', title='City Ballads', bar='Hello'),
]
def testFooItem(self):
print ('Test Foo Item')
item = self.__class__.session.query(FooItem).filter(FooItem.key == 'f1').first()
assert item.title == 'Possession', 'Title mismatch'
def testBarItem(self):
print ('Test Bar Item')
item = self.__class__.session.query(BarItem).filter(BarItem.key == 'b2').first()
assert item.title == 'City Ballads', 'Title mismatch'
def setUp(self):
if not self.__class__.is_setup:
self.__class__.session = DBSession()
self.metadata = Base.metadata
self.metadata.bind = engine
self.metadata.drop_all() # Drop table
self.metadata.create_all() # Create tables
self.__class__.session.add_all(self.test_items) # Add data
self.__class__.session.commit() # Commit
self.__class__.is_setup = True
def tearDown(self):
if self.__class__.is_setup:
self.__class__.session.close()
# Just for Python >=2.7 or >=3.2
#classmethod
def setUpClass(cls):
pass
#Just for Python >=2.7 or >=3.2
#classmethod
def tearDownClass(cls):
pass
if __name__ == '__main__':
unittest.main()
The most likely reason for this behavior is the fact that that data is not properly cleaned up between the tests. This explains why when you run only one test, it works.
setUp is called before every test, and tearDown - after.
Depending on what you would like to achieve, you have two options:
create data only once for all test.
In this case you if you had Python-2.7+ or Python-3.2+, you could use tearDownClass method. In your case you can handle it with a boolean class variable to prevent the code you have in setUp running more then once.
re-create data before every test
In this case you need to make sure that in the tearDown you delete all the data. This is what you are not doing right now, and I suspect that when the second test is ran, the call to one() fails not because it does not find an object, but because it finds more two objects matching the criteria.
Check the output of this code to understand the call sequence:
import unittest
class TestSchema(unittest.TestCase):
def testOne(self):
print '==testOne'
def testTwo(self):
print '==testTwo'
def setUp(self):
print '>>setUp'
def tearDown(self):
print '<<tearDown'
#classmethod
def setUpClass():
print '>>setUpClass'
#classmethod
def tearDownClass():
print '<<tearDownClass'
if __name__ == '__main__':
unittest.main()
Output:
>>setUp
==testOne
<<tearDown
>>setUp
==testTwo
<<tearDown
I have this as my tearDown method and it does work fine for my tests:
def tearDown (self):
"""Cleans up after each test case."""
sqlalchemy.orm.clear_mappers()

Django Extended User Profile Sign Up Form Returns IntegrityError

So, I'm new to Django and this has become a major headache. So I'm trying to create an extended user profile a la the documentation. However, when I attempt to post an entry through the form I receive the following error:
Exception Type: IntegrityError at /signup/ Exception Value: (1062,
"Duplicate entry '64' for key 2")
Upon crashing with an Internal Server Error, I notice that a new user with the relevant fields (username, password, first name, last name) is created and so is the profile entry. However the profile entry is devoid of all the information entered, with the exception of the id of the user the profile is associated with. Here is the relevant code:
In views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
from portalman.models import FnbuserForm, AuthUserForm, Fnbuser
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from django.db import IntegrityError
from django.utils.safestring import mark_safe
from django.contrib.auth.models import User
def signup(request):
viewParam = {}
errors = None
profileForm = FnbuserForm()
userForm = AuthUserForm()
if request.method == "POST":
profileForm = FnbuserForm(request.POST)
userForm = AuthUserForm(request.POST)
if userForm.is_valid() and profileForm.is_valid():
newuser = User.objects.create_user(userForm.cleaned_data['username'], userForm.cleaned_data['email'], userForm.cleaned_data['password'])
newuser.first_name = userForm.cleaned_data['first_name']
newuser.last_name = userForm.cleaned_data['last_name']
newuser.save()
newFnbUser = Fnbuser(user = newuser, #other profile elements#)
newFnbUser.save()
return HttpResponseRedirect('/thanks/')
else:
profileForm = FnbuserForm() # An unbound profile form
userForm = AuthUserForm() #An unbound user form
else:
viewParam = {'profileForm': profileForm, 'userForm' : userForm, 'links' : links}
viewParam.update(csrf(request))
return render_to_response('signup.html', viewParam, context_instance=RequestContext(request))
In portalman/models.py:
from django.db import models
from django import forms
from django.contrib.auth.models import User, UserManager
from django.db.models.signals import post_save
class Fnbuser(models.Model):
telephone = models.CharField(max_length=20, blank=True, null=True)
additionalNotes = models.TextField(help_text=("Feel free to add additional notes or comments here!"), blank=True,
verbose_name="Notes", null=True)
canCook = models.BooleanField(verbose_name="Can Cook", default=False, blank=True)
canTransport = models.BooleanField(verbose_name="Can Tranport", default=False, blank=True)
canServe = models.BooleanField(verbose_name="Can Serve Food", default=False, blank=True)
canGarden = models.BooleanField(verbose_name="Can Garden", default=False, blank=True)
canProgram = models.BooleanField(verbose_name="Can Program", default=False, blank=True)
canDesign = models.BooleanField(verbose_name="Can Design", default=False, blank=True)
canAlso = models.CharField(verbose_name="Other", max_length=255, blank=True, null=True)
user = models.ForeignKey(User, unique=True)
def __str__(self):
return self.user.username
def create_user_profile(sender, instance, created, **kwargs):
if created:
Fnbuser.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
class Meta:
verbose_name = "Food Not Bombs User"
verbose_name_plural = "Food Not Bombs Users"
class AuthUserForm(forms.Form):
username = forms.RegexField(label=("*Username:"), max_length=30, regex=r'^[\w.#+-]+$',
error_messages = {'invalid': ("This value may contain only letters, numbers and # . + - _ characters."),
'inUse' : ("This username is already in use, try another.")},
help_text = ("Required. 30 characters or fewer. Letters, digits and # . + - _ only."))
password = forms.CharField(label=("*Password:"), widget=forms.PasswordInput,
help_text = ("Required. 30 characters or fewer. Letters, digits and # . + - _ only."),
error_messages = {'invalid': ("This value may contain only letters, numbers and # . + - _ characters.")})
email = forms.CharField(label=("E-Mail Address:"), required=False)
first_name = forms.CharField(label=("First Name:"), required=False)
last_name = forms.CharField(label=("Last Name:"), required=False)
class FnbuserForm(forms.Form):
telephone = forms.CharField(label=("Telephone:"), required=False)
additionalNotes = forms.CharField(label=("Additional Notes about yourself, or general comments."),
widget = forms.widgets.Textarea(), required=False)
canCook = forms.BooleanField(label=("Can Help Cook:"), required=False)
canTransport = forms.BooleanField(label=("Can Help Transport:"), required=False)
canServe = forms.BooleanField(label=("Can Help Serve Food:"), required=False)
canGarden = forms.BooleanField(label=("Can Help Garden:"), required=False)
canProgram = forms.BooleanField(label=("Can Help Program Site:"), help_text=("""If you can help program the site,
note what languages you can program in within the "can also help with" box."""), required=False)
canDesign = forms.BooleanField(label=("Can Help With Design:"), help_text=("""Photography/Video Editing/Web Design/etc. Note
what you can do specifically within the "can also help with" box."""), required=False)
canAlso = forms.CharField(label=("Can Also Help With:"), required=False)
class Meta:
model=Fnbuser
Could anyone suggest what is going wrong? I would be very grateful!
You can't use:
Fnbuser.objects.create(user=instance)
after the object has already been created with:
newFnbUser.save()
That's why the call to the create method in the QuerySet (objects) will raise IntegrityError.
see doc