Django forms update instance while keep date/time unchanged - django-forms

I've created a model for properties and connected it to a form. I started to render the form with template tags using {{ form.as_p }} and everything worked fine when I used my view for editing/updating an instance. But I wanted to customize my html so I manually rendered all form fields and now there is an issue when I try to update/edit a specific instance. The error says:
list_date
Enter a valid date/time
The thing is that I don't want the list_date to be able to be update by the user so I just have it as a hidden input (list_date should be the value as when the instance was created). Is it something in the html that is wrong? Can I solve this by handling it in my view funtion updateProperty?
models.py
from django.db import models
from django.utils import timezone
from accounts.models import User
class Property(models.Model):
realtor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, default=User)
title = models.CharField(max_length=200)
property_type = models.CharField(max_length=200)
address = models.CharField(max_length=200)
area = models.CharField(max_length=200, blank=True)
city = models.CharField(max_length=200)
county = models.CharField(max_length=200, blank=True)
municipality = models.CharField(max_length=200, blank=True)
zipcode = models.CharField(max_length=200, blank=True)
floor = models.IntegerField(blank=True)
description = models.TextField(blank=True)
price = models.IntegerField()
rooms = models.IntegerField()
square_meter = models.IntegerField()
balcony = models.BooleanField(blank=True)
elevator = models.BooleanField(blank=True)
fee = models.IntegerField(blank=True)
photo_main = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
photo1 = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
photo2 = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
is_published = models.BooleanField(default=True)
list_date = models.DateTimeField(default=timezone.now, blank=True)
viewing_date = models.DateTimeField(timezone.now, null=True, blank=True)
viewing_date2 = models.DateTimeField(timezone.now, null=True, blank=True)
KOMMANDE = 'KO'
TILL_SALU = 'TS'
BUDGIVNING = 'BG'
SALD = 'SA'
PROPERTY_STATUS = [
(KOMMANDE, 'Kommande'),
(TILL_SALU, 'Till Salu'),
(BUDGIVNING, 'Budgivning'),
(SALD, 'Såld'),
]
property_status = models.CharField(
max_length=2,
choices=PROPERTY_STATUS,
default=KOMMANDE,
)
def __str__(self):
return self.title
forms.py
from django.forms import DateInput, ModelForm
from .models import Property
class PropertyForm(ModelForm):
class Meta:
model = Property
fields = '__all__'
widgets = {'list_date' : DateInput()}
views.py
from django.shortcuts import get_object_or_404, render, redirect
from .models import Property
from .forms import PropertyForm
from django.utils import timezone
def createProperty(request):
form = PropertyForm()
if request.method == "POST":
form = PropertyForm(request.POST)
print(request.POST)
if form.is_valid():
new_property = form.save(commit=False)
new_property.list_date = timezone.now()
new_property.realtor = request.user
new_property.save()
print(new_property.list_date)
return redirect('/properties/')
context = {'form':form}
return render(request, 'properties/property_form.html', context)
def updateProperty(request, pk):
property = Property.objects.get(id=pk)
form = PropertyForm(instance=property)
if request.method == "POST":
form = PropertyForm(request.POST, instance=property)
if form.is_valid():
updated_property = form.save(commit=False)
updated_property.save()
return redirect('/properties/')
context = {'form':form, 'property':property}
return render(request, 'properties/property_form.html', context)
property_form.html (only some parts)
<label for="{{ form.list_date.id_for_label }}" hidden></label>
<input type="hidden" name="{{ form.list_date.html_name }}" id="{{ form.list_date.id_for_label }}" {% if property %} value="{{ property.list_date }}" {% endif %}>
I want the list_date to be unchanged when editing and submitting the form.
I've tried to render the list_date in the hidden input as above. I'm very grateful for any solutions that will help me.

Related

How to show the forms field so user can fill

I am trying to create a form where user can create a team. I did the view, the model and the form, But, somehow the template return with only a submit button and I don't know how to solve it. Any help is of great help, thanks.
Here is the view:
def team_create_view(request):
title = 'Create'
form = TeamCreateForm(request.POST or None, request.FILES or None)
coach = get_coach(request.user)
if request.method == 'POST':
if form.is_valid():
form.instance.coach = coach
form.save()
return redirect(reverse("club", kwargs={ 'id': form.instance.id }))
context = {
'title': title,
'form': form
}
return render(request, "team_create.html", context)
Now the form:
class TeamCreateForm(forms.Form):
form = forms.CharField(widget=forms.Textarea)
class Meta:
model = Team
fields = ('form', 'name', 'logo', 'birth', 'coach', 'players')
The model:
class Team(models.Model):
name = models.CharField(max_length=200)
logo = models.ImageField(default="", blank=True, null=True)
birth = models.DateField(default="", blank=True, null=True)
coach = models.ForeignKey(User, on_delete=models.CASCADE, default="", blank=True, null=True)
players = models.ManyToManyField(User, related_name='players')
def __str__(self):
return self.name
And finally the form:
You form render is written in inside the POST method condition. So in GET method does not render.Please try this.
First Method
Here we just change render code to outside of POST method checking if condition.
def team_create_view(request):
title = 'Create'
form = TeamCreateForm(request.POST or None, request.FILES or None)
coach = get_coach(request.user)
if request.method == 'POST':
if form.is_valid():
form.instance.coach = coach
form.save()
return redirect(reverse("club", kwargs={'id': form.instance.id }))
context = {'title': title,'form': form}
return render(request, "team_create.html", context)
Second Method
Here we check if GET method.
def team_create_view(request):
title = 'Create'
form = TeamCreateForm(request.POST or None, request.FILES or None)
coach = get_coach(request.user)
if request.method == 'POST':
if form.is_valid():
form.instance.coach = coach
form.save()
return redirect(reverse("club", kwargs={'id': form.instance.id }))
elif request.method == "GET":
context = {'title': title,'form': form}
return render(request, "team_create.html", context)

json serialize serializer instance

I want to return some useful information after POST to my api. I have
# views.py
from .serializers import FlagInstanceSerializer
SomeViewSet(viewsets.ModelViewSet):
...
#action(detail=True, methods=['post'])
def flag(self, request, pk=None):
some_model = self.get_object()
flag_instance = flaggit.utils.flag(some_model, user=request.user, ip=None, comment=None)
serializer = FlagInstanceSerializer(data=flag_instance)
serializer.is_valid()
return Response(data=serializer.data)
model
# models.py
class FlagInstance(models.Model):
flag = models.ForeignKey(Flag, related_name='flags', on_delete=models.CASCADE)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
ip = models.GenericIPAddressField(blank=True, null=True)
datetime = models.DateTimeField(auto_now_add=True)
flag_type = models.PositiveIntegerField(choices=FLAG_TYPES, default=1)
comment = models.TextField(blank=True, null=True)
def __str__(self):
return u'%s: %s' % (self.user, self.flag.content_object)
serializer
# serializers.py
from flaggit.models import FlagInstance
class FlagInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = FlagInstance
fields = '__all__'
The rest docs show the following sanity check.
In [1]: from polls.serializers import FlagInstanceSerializer
In [2]: serializer = FlagInstanceSerializer()
In [3]: print(repr(serializer))
FlagInstanceSerializer():
id = IntegerField(label='ID', read_only=True)
ip = IPAddressField(allow_null=True, required=False)
datetime = DateTimeField(read_only=True)
flag_type = ChoiceField(choices=((1, 'Inappropriate'), (2, 'Move To Jobs'), (3, 'Move To Events'), (4, 'Move To Promotions')), required=False, validators=[<django.core.validators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
comment = CharField(allow_blank=True, allow_null=True, required=False, style={'base_template': 'textarea.html'})
flag = PrimaryKeyRelatedField(queryset=Flag.objects.all())
user = PrimaryKeyRelatedField(allow_null=True, queryset=User.objects.all(), required=False)
The api responds with {}. I would like the api to respond with a json representation of a FlagInstance object.
You need to actually put an instance "into" the serializer to serialize the values into JSON. You are currently modifying a property that is used to deserialize the data into python objects.
Change your viewset method to this, replacing data with instance:
#action(detail=True, methods=['post'])
def flag(self, request, pk=None):
some_model = self.get_object()
flag_instance = flaggit.utils.flag(some_model, user=request.user, ip=None, comment=None)
serializer = FlagInstanceSerializer(instance=flag_instance)
return Response(data=serializer.data)

autocomplete-light for adding popup outside the admin

I'm using django-crispy-forms and would like to use autocomplete-light but can't get it going. I need users to be able to create a new facility if the one they want doesn't exist.
I just have no idea how to use autocomplete-light and I've been struggling for days. Can someone please point me in the right direction??
models.py
class CollectionFacility(TimeStampedModel):
"""
Data collection facility.
"""
facility_name = models.CharField(max_length=256, blank=False)
address_line1 = models.CharField("Address line 1", max_length=45)
address_line2 = models.CharField("Address line 2", max_length=45, blank=True)
country = models.CharField(max_length=50, blank=False)
state_province = models.CharField(max_length=100, blank=True)
city = models.CharField(max_length=100, blank=False)
postal_code = models.CharField("Postal Code", max_length=20, blank=True)
facility_contact = models.ForeignKey('FacilityContact', related_name='collection_facilities', null=True, blank=True)
def __unicode__(self):
return "%s, %s" % (self.facility_name, self.country)
class Meta:
ordering = ['country', 'facility_name', 'city', 'state_province']
verbose_name = "Collection Facility"
verbose_name_plural = "Collection Facilities"
class FacilityContact(TimeStampedModel):
TITLES = (
('Mrs.', 'Mrs.'),
('Ms.', 'Ms.'),
('Mr.', 'Mr.'),
('Dr.', 'Dr.'),
)
first_name = models.CharField(max_length=256, blank=False)
middle_initial = models.CharField(max_length=4, blank=True)
last_name = models.CharField(max_length=256, blank=False)
title = models.CharField(max_length=4, choices=TITLES, blank=True)
email = models.EmailField(blank=False)
def __unicode__(self):
return "%s, %s" % (self.last_name, self.first_name)
class Meta:
ordering = ['last_name', 'first_name']
verbose_name = "Facility Contact"
verbose_name_plural = "Facility Contacts"
forms.py
class FacilityForm(autocomplete_light.ModelForm):
class Meta:
model = CollectionFacility
views.py
facility_form = FacilityForm()
# pass it in the context to template
....
template.html
{% crispy facility_form %}
Did you check the non_admin_add_another example app ?
Docs about that one have not yet been ported to v2 which mean the code in the docs might not work. However note that autocomplete_light.example_apps.non_admin_add_another should work.
I recommend you start fiddling with that example directly in autocomplete_light's test_project, see: http://django-autocomplete-light.readthedocs.org/en/stable-2.x.x/demo.html

Django Frontend Inline Form with related models

I'm a python/django newbie. Sorry for my english I'm not a native english speaker. I am trying to make a form where users can add in related models. On the admin site I am able to do this however I am having a hard time implementing the same thing on the website end.
Below is the admin screenshot. I want to implement on the front end the same thing where there's a way to add New Artist and New Tags to the form.
http://postimg.org/image/lv83s9fq5/
Here's my models.py
from django.db import models
from uuslug import uuslug
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class Tag(models.Model):
name = models.CharField(max_length=200, unique=True)
slug = models.CharField(max_length=200, unique=True)
class Meta:
ordering = ["name"]
def __str__(self):
return self.slug
class Artist(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.CharField(max_length=100)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class Song(models.Model):
S_KEYS = (
('C', 'C'),
('C#', 'C#'),
('D', 'D'),
('D#', 'D#'),
('E', 'E'),
('F', 'F'),
('F#', 'F#'),
('G', 'G'),
('G#', 'G#'),
('A', 'A'),
('A#', 'A#'),
('B', 'B'),
)
title = models.CharField(max_length=200)
artist = models.ForeignKey(Artist)
user = models.ForeignKey(User)
song_key = models.CharField(max_length=2, choices=S_KEYS)
body = models.TextField()
views = models.IntegerField(default=0)
slug = models.CharField(max_length=100, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
tags = models.ManyToManyField(Tag, blank=True)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = uuslug(self.title, instance=self, max_length=100)
super(Song, self).save(*args, **kwargs)
def add_view_count(self):
if self.views is not None:
self.views +=1
Here's what's in my views.py
class SongAdd(generic.CreateView):
template_name= 'song/add.html'
model = Song
form_class = SongForm
success_url = '/'
def form_valid(self, form):
form.instance.user = self.request.user
return super(SongAdd, self).form_valid(form)
I hope you can help me :)
Thanks in advance!
Use django-dynamic-formset app, the same that admin use with the name inlines.js. It has good examples for exactly what you need.

Different user profiles with django-profiles & django-registration

My models.py:
USER_TYPES = (
('D', 'Demo' ),
('F', 'Free' ),
('P', 'Premium'),
)
class BaseProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
user_type = models.CharField(max_length=1, blank=True, choices=USER_TYPES)
class DemoProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
demo = models.CharField(max_length=10, blank=True)
...
class FreeProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
free = models.CharField(max_length=10, blank=True)
...
class PremiumProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
premium = models.CharField(max_length=10, blank=True)
...
class ProxyProfile(BaseProfile):
class Meta:
proxy = True
def get_profile(self):
if self.user_type == 'D':
return DemoProfile._default_manager.get(user__id__exact=self.user_id)
elif self.user_type == 'F':
return FreeProfile._default_manager.get(user__id__exact=self.user_id)
else:
return PremiumProfile._default_manager.get(user__id__exact=self.user_id)
I use BaseProfile to map user_id to specific user_type. I wanted to use ProxyProfile as proxy which loads user_type depending profiles to ModelForm as shown below
Content of my forms.py:
class ProfileForm(ModelForm):
...
class Meta:
model = ProxyProfile
exclude = ('user','user_type')
...
ProfileForm is provided to django-profiles using following code in urls.py:
urlpatterns += patterns('',
url(r'^profiles/edit/', edit_profile,
{'form_class': ProfileForm},
name='profiles_edit_profile'),
(r'^profiles/',include('profiles.urls')),
)
I've also set in settings.py:
AUTH_PROFILE_MODULE = 'main.ProxyProfile'
During user registration all db data is filled correctly (it looks like everything is OK).
I register using form passed to django-registration:
urlpatterns += patterns('',
url(r'^register/$', register,
{'form_class': UserRegistrationForm},
name='registration.views.register'),
(r'', include('registration.urls')),
)
from forms.py:
class UserRegistrationForm(RegistrationFormUniqueEmail, RegistrationFormTermsOfService):
utype = forms.ChoiceField(choices=USER_CHOICES)
def save(self, profile_callback=None):
new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
password.self.cleaned_data['password1'],
email=self.cleaned_data['email'],
)
new_base_profile = BaseProfile(user=new_user, user_type=self.cleaned_data['utype'])
if self.cleaned_data['utype'] == "D":
new_profile = DemoProfile(user=new_user)
if self.cleaned_data['utype'] == "F":
new_profile = FreeProfile(user=new_user)
if self.cleaned_data['utype'] == "P":
new_profile = PremiumProfile(user=new_user)
new_profile.save()
new_base_profile.save()
return new_user
And registration phase works OK.
I've problem with profile edit/details pages.
My profiles filtered in ProxyProfile model and used as FormModel in ProfileForm
are not rendered (I can't see profile specific fields are not rendered to HTML page)
Maybe there is some other way (more like Django way :)) to do this
(select and render profile model depending on user_type field which is related to User model).
Thanks in advance :)
Ok, finally I've had an idea how I can do this :)
In my models.py:
class BaseManager(models.Manager):
def get(self, **kwargs):
self.u = kwargs['user__id__exact']
self.bt = BaseProfile.manager.get(user__id__exact=self.u)
if self.bt.user_type == 'F':
return FreeProfile.objects.get(pk=self.u)
elif self.bt.user_type == 'I':
return PremiumProfile.objects.get(pk=self.u)
else:
return None
class BaseProfile(models.Model):
objects = BaseManager()
manager = UserManager()
user = models.OneToOneField(User, primary_key=True)
user_type = models.CharField(max_length=1, blank=True, choices=USER_TYPES)
class FreeProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
free = models.CharField(max_length=10, blank=True)
...
class PremiumProfile(models.Model):
user = models.OneToOneField(User, primary_key=True)
premium = models.CharField(max_length=10, blank=True)
...
In custom manager - BaseManager I return profile object by overwriting get() method used by get_profile. I have to use UserManager named simply 'manager' to prevent recursive call of custom manager when assigning self.bt
OK, this is a half way to achive what I want, now I can view different profiles attached to users using django-profiles app.
Next, I want to use ModelForm to prepare edit form for user profiles. Users can have different profiles so I've applied the magic trick presented in this snippet: http://djangosnippets.org/snippets/2081/
And now in my forms.py I have:
class FreeForm(forms.ModelForm):
class Meta:
model = FreeProfile
class PremiumForm(forms.ModelForm):
class Meta:
model = PremiumProfile
next, simple model forms for each profile are assembled in ProfileForm:
class ProfileForm(ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs['instance'].user
profile_kwargs = kwargs.copy()
profile_kwargs['instance'] = self.user
self.bt = BaseProfile.manager.get(user__id__exact=self.user.id)
if self.bt.user_type == 'F':
self.profile_fields = FreeForm(*args, **profile_kwargs)
elif self.bt.user_type == 'P':
self.profile_fields = PremiumForm(*args, **profile_kwargs)
super(ProfileForm, self).__init__(*args, **kwargs)
self.fields.update(self.profile_fields.fields)
self.initial.update(self.profile_fields.initial)
class Meta:
model = BaseProfile
def save(self):
...
In settings.py:
AUTH_PROFILE_MODULE = 'main.BaseProfile'
And it works like a charm but I wonder if it is the Django way to achieve support for multiple different profiles using django-profiles?
It worries me that I have to use get() few more times before I render profile details or edit form.
But after 4 days of struggling with Django to get this done finally I can sleep well tonight :)
Cheers

Resources