I have two Models in my application in Django
The Change model is only for storing the change logs made in Model Ip
My models.py
class Change(models.Model):
author = models.ForeignKey('auth.User', null=False, on_delete=models.CASCADE, default='auth.User')
ip = models.ForeignKey('Ip', on_delete=models.CASCADE, default='')
old_cluster = models.ForeignKey('Cluster', on_delete=models.CASCADE, default='')
old_status = models.ForeignKey('Status', on_delete=models.CASCADE, default='')
new_cluster = models.CharField(max_length=20)
new_status =models.CharField(max_length=20)
change_date = models.DateTimeField(default=timezone.now)
class Ip(models.Model):
author = models.ForeignKey('auth.User', null=False, on_delete=models.CASCADE, default='auth.User')
number = models.CharField(max_length=20, unique=True, default='')
status = models.ForeignKey('Status', on_delete=models.CASCADE, default='')
cluster = models.ForeignKey('Cluster', on_delete=models.CASCADE, default='')
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.number
class Meta:
ordering = ('number',)
my views.py
def ip_edit(request, id):
ip_edit = get_object_or_404(Ip, id=id)
form = EditManagementForm(request.POST, instance=ip_edit)
change_form = ChangeLogsForm()
if request.method == "POST":
if form.is_valid():
ip_edit = form.save(commit=False)
ip_edit.save()
change_form = ChangeLogsForm(request.POST)
if change_form.is_valid():
ip_change = change_form.save(commit=False)
ip_change.author = request.user
ip_change.ip = request.number
ip_change.save()
return redirect('/app/management')
else:
form = EditManagementForm(instance=ip_edit)
args = {
'form': form,
}
return render(request, 'app/ip_edit.html', args
and my forms.py
class EditManagementForm(forms.ModelForm):
class Meta:
model = Ip
fields = (
'number',
'status',
'cluster',
)
widgets = {
'number': TextInput(attrs={'class': 'ls-form-text'}),
}
class ChangeLogsForm(forms.ModelForm):
class Meta:
model = Change
fields = (
'ip',
'old_cluster',
'old_status',
'new_cluster',
'new_status',
)
when I save the ip information editing, no error occurs, but it also does not save the Model Change information
could you help me and let me know if there is any more correct and simple way to store a history of changes in a Model?
Since your Change model has a foreign key relation to Ip model and since ip_change in your view.py presents the instance of Change model then you should replace ip_change.ip = request.number with ip_change.ip = ip_edit because ip_edit is the instance of Ip model.
Do you want to perform the save operation on Ip and Change models only when both forms are valid? If you want that then this code has one really serious problem which people sometimes overlook and that problem is related to violation of database integrity. Just try to think about the situation when form.is_valid() returns True and change_form.is_valid() returns False. If that happens you will only save the data into Ip database table and nothing into Change database table because this line ip_change.save() will not be reached. If that happens the data integrity will be ruined and I guess you don't want that - you probably want to ensure that both saveoperations either executes or not. You should validate both forms at the same time like if form.is_valid() and change_form.is_valid() and put the rest of logic into that block.
P.S.
Since I started to talk about the violation of database integrity you should also have a look at atomic database transactions, it can be useful to know.
Related
I am working on a Django project where I want to search profile records for those that are resident in a particular country and state.
In this project I would be collecting data of people from different countries and their respective states, so I want a situation where I can have two Search Forms for Country and State.
The form should be able to allow me select list of countries in the Profile Model while the state form should be able to allow me type and search for a state in the selected country.
The result should be a list of persons in the selected Country and searched state.Please understand that I decided to go with Q objects because I learn that it makes queries efficient.
Here are my model code:
class Profile(models.Model):
applicant = models.OneToOneField(User, on_delete=models.CASCADE, null = True)
surname = models.CharField(max_length=10, null=True)
othernames = models.CharField(max_length=30, null=True)
gender = models.CharField(max_length=6, choices=GENDER, blank=True, null=True)
nation = models.CharField(max_length=10, choices=NATION, blank=True, null=True)
state = models.CharField(max_length=20, null=True)
address = models.CharField(max_length=200, null=True)
phone = models.CharField(max_length=11, null=True)
image = models.ImageField(default='avatar.jpg', upload_to ='profile_images')
Here is my search form code:
class Applicant_Search_Form(forms.ModelForm):
class Meta:
model = Profile
fields = ['nation', 'state']
Here is my views code:
def SearchApplicants(request):
form = Applicant_Search_Form(request.GET or None)
if form:
list_submited = Q(nation__icontains = form['nation'].value()) & Q(state__icontains = form['state'].value())
else:
list_submited = Profile.objects.all()
paginator = Paginator(list_submited, 5)
page = request.GET.get('page')
paged_listApps = paginator.get_page(page)
context = {
'list_applicants':paged_listApps,
'form':form,
}
return render(request, 'user/list_applicants.html',context)
I have tried running the above code but I am have a TypeError which says 'Q' object is not subscriptable.
Someone should kindly help with how to solve this problem and possibly the best approach to this kind of search.Thank in anticipation to your answers.
You should filter the Profile.objects.all() queryset, so:
def SearchApplicants(request):
form = Applicant_Search_Form(request.GET)
if form.is_valid():
list_submited = Profile.objects.filter(
nation__icontains=form.cleaned_data['nation'],
state__icontains=form.cleaned_data['state']
)
else:
list_submited = Profile.objects.all()
# …
Note: Functions are normally written in snake_case, not PascalCase, therefore it is
advisable to rename your function to search_applicants, not SearchApplicants.
Note: Forms in Django are written in PascalCase, not snake_case,
so you might want to rename the model from Applicant_Search_Form to ApplicantSearchForm.
I am working on a small application containing models CustomUser and PollQuestion. CustomUser having ManyToMany relation between itself and a CustomUser can have multiple PollsQuestion as well so there is Foreign Key relation between them.
An authenticated user is only able to see polls raised by users he is following, to full-fill this requirement i have written following view**:-**
Actually this is not view this is an utility method returning the polls to original view.
def all_polls_utils(request):
following = request.user.get_all_followings().values_list("id")
user_id = [id[0] for id in following]
all_polls = PollQuestion.objects.none()
for u_id in user_id:
user = CustomUser.objects.get(id=u_id)
polls = user.poll_questions.all()
all_polls = all_polls | polls
return all_polls
Main Question:- Is there in better way to do the same?
Any suggestion will be highly appretiated
I am posting the models bellow:-
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
email = models.EmailField(max_length=250, null=False)
name = models.CharField(max_length=50, null=False)
username = models.CharField(max_length=50, null=False, unique=True)
password = models.CharField(max_length=15, null=False)
user = models.ManyToManyField('self', through='Relationship', symmetrical=False, related_name='related_to')
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['name', 'email']
def get_all_polls(self):
pass
def create_relationship(self, person):
status, obj = Relationship.objects.get_or_create(
to_person=person,
from_person=self,
)
return status
def remove_relationship(self, person):
Relationship.objects.filter(
from_person=self,
to_person=person
).delete()
return 'dummy_value'
def get_all_followings(self):
return self.user.all()
class Relationship(models.Model):
from_person = models.ForeignKey(CustomUser, related_name='from_people', on_delete=models.CASCADE)
to_person = models.ForeignKey(CustomUser, related_name='to_person', on_delete=models.CASCADE)
And PollQuestion:-
class PollQuestion(models.Model):
user = models.ForeignKey(CustomUser, null=True, on_delete=models.CASCADE, related_name="poll_questions")
# Other fields
Note:- You can also suggest me a better title for this post?
Thanks in advance,
Hope to here from you soon.
Simply
def all_polls_utils(request):
all_polls_by_followed = PollQuestion.objects.filter(
user__in=request.user.get_all_followings()
)
As an aside, you should probably rename the user many-to-many in CustomUser to e.g. followed_users (with a related name followers).
forms.py
from django.forms import *
from .models import client, notes, contractor, cNotes, personnel, pNotes, estimate, job
class clientForm(self, ModelForm):
class Meta():
model = client
fields = ("contractor", "firstName", "lastName", "phoneNumber", "email", "gateCode")
#inst = client.objects.get(pk=pk)
widgets = {
'firstName': TextInput(attrs={'value' : self.instance.firstName}), 'lastName': TextInput(attrs={'value' : self.instance.lastName})
}
self.instance.firstName Needs to fetch the current first name of that instance in the model so the value of the widget when the edit form view is open is the current value (Note: setting initial= doesn't work for what I want). I have tried many Solutions but I can't seem to get any of them to work.
Edit: I tried overriding init
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['firstName'].widget.attrs.update(
{'value': self.instance.firstName},
)
but when I do this it says self is not defined.
Edit: I also tried passing the instance data by getting it in the view and passing it to the form by overriding get_form_kwargs
models.py
class client(models.Model):
#model representing client
contractor = models.ForeignKey('contractor', on_delete=models.CASCADE, blank=True, null=True)
firstName = models.CharField(max_length=50, help_text='Enter first name of client', null=True, blank=True)
lastName = models.CharField(max_length=50, help_text='Enter last name of client', null=True, blank=True)
phoneNumber = models.CharField(max_length=16, help_text='Enter number for client', null=True, blank=True)
email = models.EmailField(max_length=50, help_text='Enter email for client', blank=True, null=True)
gateCode = models.CharField(max_length=50, help_text='Enter gate code for client', blank=True, null=True)
fullName = models.CharField(max_length=50, help_text='', blank=True, null=True)
client_id = models.SlugField(max_length=50, help_text='', primary_key=True)
initialContactDate = models.DateTimeField(auto_now_add=True, blank=True, null=True)
views.py
def eClientF(request, pk=None):
inst = client.objects.get(pk=pk)
if request.method == "POST":
client_form = clientForm(request.POST, instance=inst)
estimate_form = estimateForm(request.POST, instance=inst)
notes_form = notesForm(request.POST, instance=inst)
if(client_form.is_valid() and estimate_form.is_valid() and notes_form.is_valid()):
cli = client_form.save()
estimate2 = estimate_form.save(False)
notes2 = notes_form.save(False)
estimate2.client = cli
notes2.client = cli
estimate2.save()
notes2.save()
return redirect('/main/client')
else:
client_form = clientForm(instance=inst)
estimate_form = estimateForm(instance=inst)
notes_form = notesForm(instance=inst)
args = {}
args["client_form"] = client_form
args["estimate_form"] = estimate_form
args["notes_form"] = notes_form
return render(request, "main/eClformTemp.html", args)
models.py and views.py have been reduced to relevant code only
Edit: I have modified views.py as suggested, but when I try using this form it doesn't load the page and says self is not recognized referring to froms.py where it says TextInput(attrs={'value' : self.instance.firstName})
Edit:
It won't though, I know when you past instance when creating the form in views it should populate but it doesn't. I have referenced the docs and many examples to confirm I was doing it correctly and I was but it wasn't working. To work around it I ran the server and opened the form then I inspected element and copied the html and manually added value to the html and used what I copied as the template to ensure this workaround will work. However the template cannot be manually made because it wouldn't properly reflect changes in the database. I just need to know how I can pass the current model instance into the widget attrs for value. Ik I will have to edit init and update the widget that way but my implementation wouldn't work so I'm asking how to do it correctly.
It's my first time creating a Django website with models, and in my first attempt to insert data into my table I'm getting this error.
My models are as follows:
class User(AbstractUser):
pass
#https://docs.djangoproject.com/en/3.1/topics/auth/default/
class Listing(models.Model):
listingID = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="listID")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myListing", null=True)
watchers = models.ManyToManyField(User, blank=True, related_name="watchlist")
title = models.CharField(max_length=30)
description = models.TextField()
creation_date = models.DateField(auto_now=True)
img_url = models.URLField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.title}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.SET_NULL, related_name="bidsMadeOnMe", null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myBids", null=True)
price = models.FloatField()
creation_date = models.DateField(auto_now=True, null=True)
def __str__(self):
return f"Bid={self.price}"
and the view that handles the form submission is this one:
#login_required
def create_listing(request):
if request.method == "POST":
user = User.objects.get(username=request.user.username)
l = Listing(created_by=user,
title=request.POST["title"],
description=request.POST["desc"],
# https://stackoverflow.com/questions/12176585/handling-dates-over-request-get
creation_date=models.DateTimeField(auto_now_add=True),
img_url=request.POST["image_url"]
)
l.save()
b = Bid(l,
user,
request.POST["initial_bid"],
models.DateTimeField(auto_now_add=True)
)
b.save()
return render(request, "auctions/index.html")
I know the problem is the way I'm adding the data but I can't fix it. Can someone give me some light?
Your problem (well, several actually) is this:
b = Bid(l, user, request.POST["initial_bid"], models.DateTimeField(auto_now_add=True))
You're constructing a model instance by positional arguments instead of keyword arguments. This can be done, but then the invisible "id" column that has been added to the Bid model, is the first argument.
In Django we never construct models like that, but always use keyword arguments, so we're not depending on field order:
b = Bid(listing=l, user=user, ...))
Once you're solved that, your next problem is the date field.
Don't assign fields to model instances. Fields are class declarations, they don't belong on instances. Fields describe on a class (= a Model), what kind data to expect. On the instance, you assign that data.
In this case, your definition for the field is wrong on the model and on the instance you shouldn't even assign it - it will be automatically filled.
Overall, it feels like you haven't gone through Django's tutorial or did not fully understand the concepts. I suggest you go through it.
using django 2.0.2 python3.4
skip details models.py
class Post(models.Model):
postuid = models.BigAutoField(
db_column='postUID', primary_key=True)
useruid = models.Foreignkey .... skip
content = models.Text ....
registerdate = models.Date ....
class KeepPost(models.Model):
keeppostuid = models.BigAutoField(
db_column='KeepPostUID', primary_key=True)
useruid = models.ForeignKey(
'Userinfo', db_column='UserUID', on_delete=models.CASCADE)
postuid = models.ForeignKey(
'Post', db_column='PostUID', on_delete=models.DO_NOTHING)
content = models.TextField(db_column='Content')
registerdate = models.DateTimeField(
db_column='RegisterDate')
keepdate = models.DateTimeField(
db_column='KeepDate')
class ReportPost(models.Model):
reportuid=models.BigAutoField(
db_column='ReportUID', primary_key=True)
postuid=models.ForeignKey(
'Post', db_column='PostUID', on_delete=models.DO_NOTHING)
#useruid is reporter
useruid=models.ForeignKey(
'Userinfo', db_column='UserUID', on_delete=models.CASCADE)
registerdate = models.DateTimeField(
db_column='RegisterDate'
Post to KeepPost
Post.objects.get(postuid=1) or Post.objects.filter(useruid=1)
KeepPost.objects.create(modelobjects or queryset)
get() is return model object , filter() is return queryset
if i want delete after reported
why use on_delete DO_NOTHING -> I want to keep a record even if ReportedPost is deleted
just one post deleted
ex)
postmodel = Post.objects.get(postuid=request.get("postuid"))
want that models move to KeepPost and delete
postmodel.delete()
if user want delete account
usermodel = User.objects.get(useruid=useruid)
Postquery = usermodel.post_set.all()
reportPost = ReportPost.objects.filter(Q(postuid__in=Postquery))
i think move to KeepPost after intersect Postquery and reportPost
usermodel.delete()
Here are what i might simply do.
1. Remove KeepPost model.
2. Change Post model
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts', on_delete=models.CASCADE)
content = models.TextField()
registerdate = models.DateTimeField(auto_now_add=True)
is_archived = models.BooleanField(default=False)
archived_date = models.DateTimeField()
#property
def archive_post(self):
if not self.is_archived:
self.is_archived = True
self.archived_date = datetime.now()
self.save()
Whats happening here??
First you don't need a KeepPost model it is redundant since deleting and keeping a Post object will still maintain the data.
Removed postuid = models.BigAutoField(db_column='postUID', primary_key=True). Django will automatically assign a primary key id.
Add is_archived and archived_date the simpliest way to keep an instance is not to delete it at all.
Now in your view you can simple use this, either you get your source from single instance or queryset.
post = Post.objects.get(pk=1) # or id=1
post.archive_post
OR
posts = Post.objects.filter(user_id=1)
for post in posts:
post.archive_post
If you want to delete a Post instance forever then call .delete() method.