Django temporary fields only upon creation - django

Consider two models:
Contract
class Contract(models.Model):
employee = models.OneToOneField(Employee, on_delete=models.CASCADE)
contract_type = models.ForeignKey(ContractType, on_delete=models.CASCADE)
start_date = models.DateField()
end_date = models.DateField()
and
Employee
class Employee(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
contract = models.OneToOneField(Contract, on_delete=models.CASCADE, null=True, blank=True)
I was wondering if there's a way to add temporary fields in a model form that are only shown upon creation in the Employee model so I can add all those fields while I create a new Employee then when saving, I first create an Employee object, then automatically generate a Contract object and edit the original Employee object and link the now generated Contract object.
I really hope this makes sense, and if it does, can someone guide me in the right direction?
Thank you, for your time reading this.

First of all, you here made two relations between Employee and Contract. Django automatically makes a relation in reverse, so you probably should remove one. Otherwise it is possible that some_contract.emplyee.contract is not the same as some_contract.
You thus for example might want to re-model this to:
class Employee(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
class Contract(models.Model):
employee = models.OneToOneField(Employee, on_delete=models.CASCADE, related_name='contract')
contract_type = models.ForeignKey(ContractType, on_delete=models.CASCADE)
start_date = models.DateField()
end_date = models.DateField()
You can just create two ModelForms:
# app/forms.py
from app.models import Employee, Contract
class EmployeeForm(ModelForm):
class Meta:
model = Employee
exclude = ['contract']
class ContractForm(ModelForm):
class Meta:
model = Contract
Then we can render the two forms:
# app/views.py
from django.shortcuts import redirect, render
from app.forms import EmployeeForm, ContractForm
def some_view(request):
if request.method == 'POST':
employee_form = EmployeeForm(request.POST)
contract_form = ContractForm(request.POST)
if employee_form.is_valid() and contract_form.is_valid():
employee = employee_form.save()
contract_form.instance.employee = employee
contract_form.save()
return redirect('some-view')
else:
employee_form = EmployeeForm()
contract_form = ContractForm()
return render(
request,
'some_template.html',
{'employee_form': employee_form, 'contract_form': contract_form}
)
in the template, we then render the two forms in the same <form> tag:
<!-- some_template.html -->
<form method="post">
{% csrf_token %}
{{ employee_form }}
{{ contract_form }}
<button type="submit">submit</button>
</form>

Related

Create Single Forms by Two Models in Django with Dynamic Fields

I want to create Django Forms to save transactions for the store. I want a final template similar to this. The ER Diagram for my database is also shown in the image.
My Django Models are:
class Party(models.Model):
party_id = models.BigAutoField(primary_key=True)
party_name = models.CharField(max_length=128)
party_phone = models.CharField(max_length=128)
party_address = models.CharField(max_length=128)
def __str__(self):
return self.party_name
class Items(models.Model):
item_no = models.BigAutoField(primary_key=True)
item_type = models.BooleanField(default=True)
item_name = models.CharField(max_length=128)
item_qty = models.PositiveIntegerField(default=0)
item_cost = models.PositiveIntegerField(default=0)
def __str__(self):
return self.item_name
class Sales(models.Model):
invoice_no = models.BigAutoField(primary_key=True)
invoice_date = models.DateField(default=date.today)
party = models.ForeignKey(Party, on_delete=models.CASCADE)
def __str__(self):
return str(self.invoice_no)
class SalesTransaction(models.Model):
sales = models.ForeignKey(Sales, on_delete=models.CASCADE)
items = models.ForeignKey(Items, on_delete=models.CASCADE)
purchase_qty = models.PositiveIntegerField(default=0)
total_cost = models.PositiveIntegerField(default=0)
def __str__(self):
return self.item_name
I can achieve this with AJAX but I don't think it is the best solution because lots of validations have to be made with hardcoding.
How can I create Django Form to save data in that multiple tables in a single Save Click?
I have been looking for a way to do the same thing recently. The solution I stumbled upon was to use two form objects under a single <form> tag. You can differentiate between the form objects by giving them prefix attributes before passing to context. For example:
def get_view(request):
....
form1 = Form1(prefix="form1")
form2 = Form2(prefix="form2")
return render(request, 'template.html', {'form1': form1, 'form2': form2})
In your template, you would call them as usual under a single form tag:
<form>
{{ form1.as_p }}
{{ form2.as_p }}
<input type='submit'>
</form>
Now in your post view, you can use the prefix to get data of each form individually:
def post_view(request):
form1 = Form1(request.POST, prefix="form1")
form2 = Form2(request.POST, prefix="form2")
....

Rendering a custom tag with a foreign key into templates issues

Hi this is the model I am working with
from django.db import models
from users.models import CustomUser
class Project(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(CustomUser, on_delete=models.PROTECT, editable=False)
name = models.CharField(max_length=20, editable=False)
total = models.DecimalField(max_digits=6, decimal_places=2, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False, null=False, blank=False)
def __str__(self):
return self.name
The class is populated by an HTML form using this view:
def homepage(request):
if request.method == "POST":
project = Project()
name = request.POST.get('name')
total = request.POST.get('total')
created = datetime.datetime.now()
user = request.user
project.user = user
project.name = name
project.total = total
project.created = created
project.save()
#return HttpResponse(reverse("homepage.views.homepage"))
return render(request, 'homepage.html')
else:
return render(request, 'homepage.html')
and so I have added a custom tag into my app which is a function
#register.filter
def monthlyTotal(user):
this_month = now().month
return Project.objects.filter(
created__month=this_month,
user=user
).aggregate(
sum_total=Sum('total')
)['sum_total']
I call the tag like this in template
<p>Total monthly sales = {{ user.username|monthlyTotal }}</p>
however I get an error saying Field ID expected a number but got 'grandmaster' which is the name of my test user who has multiple Project objects.. if I switch to user.id I get no error but it displays None which makes sense because when I look at my project section in admin the field user is populated by the username not the id so there would be no project where user=id
You need to use the user, not the username, so:
<p>Total monthly sales = {{ user|monthlyTotal }}</p>

How to properly use filter method with foreign key in django details view with multiple models

I have two model one model is being used for storing the blog posts and another model is being used for taking the ratings and comments. Below are two my models
# Models Code
class Products(models.Model):
name = models.CharField(max_length=50)
img = models.ImageField(upload_to='productImage')
CATEGORY = (
('Snacks','Snacks'),
('Juice','Juice'),
)
category = models.CharField(max_length=50, choices=CATEGORY)
description = models.TextField()
price = models.FloatField()
review = models.TextField()
# Rating Model
class Rating(models.Model):
product = models.ForeignKey(Products, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stars = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)])
comment = models.TextField()
#Views Code
class ProductListView(ListView):
model = Products
template_name = 'products.html'
context_object_name ='Products'
class ProductDetailView(DetailView):
model = Products
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['Rating'] = Rating.objects.filter(self.product_id) # How can i get the comments only for that specific product?
return context
In details-view how should I filter to fetch the comments for that specific product only ?
no need to write separate context for that in ProductDetailView, you can do it as follows in templates
{% for rate in object.rating_set.all %}
{{ rate.comment }}
{% endfor %}

Get user OneToOneField fields in ModelForms

I've extended the django user with a OneToOneField so I can store address and such.
SiteUser is the model which extends User using a OneToOneField. How can I get fields of both User and SiteUser in a single ModelForm?
Here is the relevant code so far:
class ProfileForm(ModelForm):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class AddressForm(ModelForm):
pass
View in question:
def edit_profile(request):
username = request.user
user = User.objects.get(username__exact=username)
profileform_class = ProfileForm
if request.method == 'POST':
profileform = profileform_class(data=request.POST, instance=user)
if profileform.is_valid():
profileform.save()
return redirect('profile')
else:
profileform = profileform_class(instance=user)
return render(request, 'edit_profile.html', {
'user': user,
'profileform': profileform,
})
And the two models:
class Product(models.Model):
order = models.IntegerField(default=0)
name = models.CharField(max_length=255)
description = models.TextField()
image = models.ImageField(upload_to='product-images', default='default.jpg')
price = models.FloatField()
slug = models.SlugField(unique=True)
class SiteUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
address = models.CharField(max_length=255)
post_number = models.CharField(max_length=255, default='')
post_location = models.CharField(max_length=255, default='')
HTML Page I want the forms on:
{% extends 'base.html' %}
{% block title %}
Rediger {{ product.name }} - {{ block.super }}
{% endblock title %}
{% block content %}
<h1>Rediger "{{ user }}"</h1>
<form role="form" action="" method="post">
{% csrf_token %}
{{ profileform.as_p }}
{{ addressform.as_p }}
<button type="submit">Submit</button>
</form>
{% endblock content %}
One option is to use inline formsets. Using this, you won't be needing a second ModelForm.
Inline formsets is a small abstraction layer on top of model formsets. These simplify the case of working with related objects via a foreign key.
You can find good examples here.
Alternatively, if you want to avoid inline formsets and use both ProfileForm and AddressForm under a single <form> tag as you have done in your template, you can do it like this.
Forms:
class ProfileForm(ModelForm):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class AddressForm(ModelForm):
class Meta:
model = SiteUser
exclude = ['user']
Views:
def edit_profile(request):
username = request.user
user = User.objects.get(username__exact=username)
profileform_class = ProfileForm
addressform_class = AddressForm
if request.method == 'POST':
profileform = profileform_class(data=request.POST, instance=user)
addressform = addressform_class(data=request.POST, instance=user.siteuser)
if all((profileform.is_valid(), addressform.is_valid())):
user = profileform.save()
address = addressform.save(commit=False)
address.user = user
address.save()
return redirect('profile')
else:
profileform = profileform_class(instance=user)
addressform = addressform_class(instance=user.siteuser)
return render(request, 'edit_profile.html', {
'user': user,
'profileform': profileform,
'addressform': addressform,
})
I don't know much about forms, but I think you should use the "initial" parameter when instantiating the AddressForm, as exemplified here: https://docs.djangoproject.com/es/1.9/topics/forms/modelforms/#providing-initial-values
So you create your AddressForm class with SiteUser as model, and when you instantiate it in the view, you make it like this:
AddressForm(initial={'user': request.user})
If "username" is not the primary key of the User model, you can get the primary key like this:
User.objects.get(username=request.user).pk
and then give it in the "initial" parameter.
model:
class Company(models.Model):
name = models.CharField(max_length=150)
description = models.CharField(max_length=150)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Bike(models.Model):
to_company = models.OneToOneField(Company, on_delete=models.CASCADE, related_name="bike_company")
name_bike = models.CharField(max_length=150)
model = models.CharField(max_length=150)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
form.py
class CompanyForm(ModelForm):
class Meta:
model = Company
fields = ("name", "description" )
class BikeForm(ModelForm):
name_bike = forms.CharField(required=True)
model = forms.CharField(required=True)
class Meta(CompanyForm.Meta):
model = Company
#transaction.atomic
def save(self):
company = super().save(commit=False)
company.name = self.cleaned_data.get("name")
company.description = self.cleaned_data.get("description")
company.save()
bike = Bike.objects.create(to_company=company)
bike.name_bike = self.cleaned_data.get("name_bike")
bike.model = self.cleaned_data.get("model")
bike.save()
return company
the relationship is kept in this line:
Bike.objects.create(to_company=company)
here is an example of a different type of user model
models user and type
https://gist.github.com/josuedjh3/259b4b3b03ab195637fe2db3c701edd6
FormModel the User and UserCreationForm
https://gist.github.com/josuedjh3/0c26d989552a82d5b252c5bd3fed1054

Right way to create urls using inherit class in Django

In Django I have my app where I place information about countries and cities of these countries. This is my model.py file:
class Country(models.Model):
class Meta:
verbose_name_plural = u'Countries'
name = models.CharField(max_length=50)
slug = models.CharField(max_length=255)
description = models.TextField(max_length=10000, blank=True)
def __unicode__(self):
return self.name
class City(models.Model):
class Meta:
verbose_name_plural = u'Cities'
name = models.CharField(u'city', max_length=200)
slug = models.CharField(max_length=255, blank=True)
description = models.TextField(max_length=10000, blank=True)
country = models.ForeignKey('Country', blank=True, null=True)
def __unicode__(self):
return self.name
I have the detail view of my country, in this view there is a list of cities of this country(views.py):
def CountryDetail(request, slug):
country = get_object_or_404(Country, slug=slug)
list_cities = City.objects.filter(country=country)
return render(request, 'country/country.html', {'country':country, 'list_cities':list_cities})
this is my urls.py:
url(r'^(?P<slug>[-_\w]+)/$', views.CountryDetail, name='country'),
I want to create a url of cities which contain a slug of the country and a slug of the city, for example domain.com/spain/barcelona/.
So I created the detail view of the city, and it's looks like this:
def CityDetail(request, cityslug, countryslug):
country = Country.objects.get(slug=countryslug)
country_city = City.objects.get(country=country)
city = get_object_or_404(country_city, slug=cityslug)
return render(request, 'country/city.html', {'country':country, 'city':city})
Here is my urls.py for city detail:
url(r'^(?P<countryslug>[-_\w]+)/(?P<cityslug>[-_\w]+)$', views.CityDetail, name='resort'),
And this is how it looks like in my html file detail of the country that links to the cities:
<h1>{{country.name}}</h1>
<p>{{country.description}}</p>
<h2>Cities</h2>
{% for city in list_cities %}
<a href="/{{country.slug}}/{{city.slug}}">
<p>{{city.name}}</p>
</a>
{% endfor %}
But when I click on the link of the url of the city, I get error.
Object is of type 'City', but must be a Django Model, Manager, or QuerySet
In traceback i see that problem in CityDetail function in views.py file:
resort = get_object_or_404(country_city, slug=cityslug).
Hope you can help me with its problem. Thank You.
Yes, you can't call get_object_or_404 on an actual City object; the things that you can query are models or querysets, not instances. What do you think that call would do?
But actually the underlying problem is before that; your definition of country_city makes no sense. For a start, this is only currently working because you presumably have only one City in the country you're querying. When you have more than one, that line will fail with a MultipleObjectsReturned exception, because get can only return a single object.
You should remove that line altogether, and query the city directly from the country:
country = Country.objects.get(slug=countryslug)
city = get_object_or_404(City, country=country, slug=cityslug)