So Django has this cool feature where you can annotate a query set as in you can add attributes to each object in a query set. For example if I have a query set of users I can annotate it with number of followers which is another table with a foreign key reference to user. This can be done with the QuerySet.annotate() function. I was wondering if this is possible for a single Django object. I have a view function that gets user info, which given a unique user UUID I return the users info in the user table as well as the number of followers and followees. 1 way to do this is just query across all follower and followee table for the uuid and create a dictionary that gets returned. Or create a serializer with all the fields than annotate the single Django object like you can with a query set. Is it possible to do this?
views.py
#api_view(['GET'])
def get_user_info(request, user_uuid):
is_current_user = user_uuid == str(request.user.uuid)
# Return all user info including # of followers and followees
models.py
class User(AbstractDatesModel):
uuid = models.UUIDField(primary_key=True)
username = models.CharField(max_length=USERNAME_MAX_LEN, unique=True, validators=[
MinLengthValidator(USERNAME_MIN_LEN)])
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
avatar = models.ImageField(upload_to=avatar_directory_path, blank=True, null=True)
class FollowUser(AbstractSimpleModel):
follower = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='follower_id')
followee = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='followee_id')
I tried this in views2.py, but the issue is that it doesn't properly just return the avatar url. serializer.serialize keeps avatar as an ImageFieldFile in the serialized user object.
views2.py
#api_view(['GET'])
def get_user_info(request, user_uuid):
is_current_user = user_uuid == str(request.user.uuid)
if is_current_user:
user_object = request.user
else:
try:
user_object = User.objects.get(uuid=user_uuid)
except User.DoesNotExist as e:
return Response(dict(error=str(e),
user_message='User does not exist.'),
status=status.HTTP_404_NOT_FOUND)
user_dict = model_to_dict(user_object)
user_dict['follower_count'] = len(FollowUser.objects.filter(followee=user_uuid))
user_dict['followee_count'] = len(FollowUser.objects.filter(follower=user_uuid))
user_dict['is_following'] = FollowUser.objects.filter(followee=user_uuid, follower=str(request.user.uuid)).exists()
return Response(user_dict, status=status.HTTP_200_OK)
#api_view(['GET'])
def get_user_info(request, user_uuid):
query = User.objects.filter(pk=user_uuid)
if query.exists():
query_annotated = query.annotate(
follower_count=Count('followee_id', distinct=True),
followee_count=Count('follower_id', distinct=True),
is_following=Count('followee_id', filter=Q(follower_id=str(request.user.uuid))))
else:
return Response(dict(error=str('User not found.'),
user_message='User not found.'),
status=status.HTTP_404_NOT_FOUND)
serializer = FullUserSerializer(query_annotated[0])
return Response(serializer.data, status=status.HTTP_200_OK)
Maybe a solution? But is_following isn't returning the correct values in my tests.
Related
i just created a custom user model from abstractuser. I can create user but update is not working showing some error. I am also week in english so idk how to share my problem. In short hand i want to edit email and password of a user.
###This is my user model
class User(AbstractUser):
roles =(
('Admin','Admin'),
('Placement Manager','Placement Manager'),
)
username=models.CharField(max_length=100,null=True,blank=True)
email = models.EmailField(max_length=50, null=True,blank=True)
phone = models.IntegerField(unique=True)
role = models.CharField(max_length=100,choices = roles,null=True,blank=False)
USERNAME_FIELD = 'phone'
REQUIRED_FIELDS = ['email','username','role']
objects=UserManager()
def get_username(self):
return self.email
###This is my view
def editPlacementManager(request):
if request.method=='POST':
name=request.POST.get('name')
phone=request.POST.get('phone')
email=request.POST.get('email')
password=request.POST.get('password')
userid = request.POST.get('pmId')
User.objects.get(id=userid).update(username=name,phone=phone,email=email,password=password,role='Placement Manager')
return redirect('listplacementmanager')
return render(request,"index/placementmanager.html")
### The error is
AttributeError at /editplacementmanager
'User' object has no attribute 'update'
Yh, you'll get that error because the update method is available for only querysets.
So you can do this:
def editPlacementManager(request):
if request.method=='POST':
name=request.POST.get('name')
phone=request.POST.get('phone')
email=request.POST.get('email')
password=request.POST.get('password')
userid = request.POST.get('pmId')
user = User.objects.get(id=userid)
user.name = name
user.phone = phone
user.email = email
# You can continue with whichever field you want
user.save()
# Then finally you save the object with the updated fields
return redirect('listplacementmanager')
return render(request,"index/placementmanager.html")
actually it's a simple thing just hash the password and update.
I have two Django class,I want to filter VCDUnavailAudit by site_key in Django Get method, how could I do
enter code here
class VCDUnavailAudit(models.Model):
user_key = models.IntegerField()
user_name = models.CharField(max_length=40)
class User(models.Model):
user_key = models.AutoField(primary_key=True)
site_key = models.IntegerField()
#api_view(['GET'])
def get_unavail_audit_records_by_site(request, site_key: int):
availability_audit_records = VCDUnavailAudit.objects.filter
serializer = VCDUnavailAuditSerializer(availability_audit_records, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
First create a list all user_keys collected from users that have the given site_key.
user_keys = [user.user_key for user in User.objects.filter(site_key=site_key)]
Then get all the availibilty audit records that have their user_key included in the user_keys list.
audits = VCDUnavailAudit.objects.filter(user_key__in=user_keys)
Complete view:
#api_view(['GET'])
def get_unavail_audit_records_by_site(request, site_key: int):
user_keys = [user.user_key for user in User.objects.filter(site_key=site_key)]
audits = VCDUnavailAudit.objects.filter(user_key__in=user_keys)
serializer = VCDUnavailAuditSerializer(audits, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have created two models Leads and Deals, and I have coded some logic such that if you click a button the Lead becomes a Deal, so what I want it is that a new form is presented to the user but that form already contains the information from the Leads model.
#login_required
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
print(obj.expected_revenue)
form = NewDealForm(request.POST or None, instance=obj)
return render(request,
"account/close_lead.html",
{'form':form})
I have done some debug and printed to the console the queryset and the information is fine, so the queryset is no the problem, the problem is that the NewForm doesn't prepopulate the new values.
models.py (only 2 models shown)
class Leads(models.Model):
CHOICES = (
('Illumination Studies','Illumination Studies'),
('Training','Training'),
('Survey Design','Survey Design'),
('Software License','Software License')
)
STATUS = (('Open','Open'),
('Closed','Closed'),
('Canceled', 'Canceled')
)
project_id = models.BigAutoField(primary_key=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
created_at = models.DateTimeField(auto_now_add=True)
point_of_contact = models.ForeignKey(Client, on_delete=models.CASCADE)
expected_revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
expected_licenses = models.IntegerField(blank=True)
country = CountryField(blank_label='(select country)')
status = models.CharField(max_length=10,choices=STATUS)
estimated_closing_date = models.DateField(blank=True)
services = models.CharField(max_length=20,choices=CHOICES)
def __str__(self):
return f'{self.company}'
class Deal(models.Model):
project_id = models.ForeignKey(Leads, on_delete=models.CASCADE, default='id')
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
service = models.ForeignKey(Leads, on_delete=models.CASCADE, related_name='service')
closing_date = models.DateField(auto_now_add=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE,default='client')
licenses = models.ForeignKey(Leads,on_delete=models.CASCADE, related_name='licenses')
revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
comments = models.TextField(blank=True,null=True)
Now, it could be that I have to inherit from a different form?
forms.py (only NewDealForm)
class NewDealForm(forms.ModelForm):
class Meta:
model = Deal
fields = ['agent','client','project_id','service', 'licenses','revenue', 'comments']
Obviously, worst-case scenario is to create a dictionary to extract the data from the queryset and then pass it to the form, but I'm sure Django has a more elegant way to handle this process.
Well, I guess sometimes Stack Overflow pushes you to solve your own issues, this is the solution.
Essentially, the initial=queryset value was not initializing the form mainly because I have very specific relationships in my model, so what I did is to create a dictionary (key:value) with the form field as key, and my queryset from my model as the value, the code is as below:
'''
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
m = obj.__dict__
keys = Leads.objects.get(project_id=m['project_id'])
form_dict = {'project_id':keys.project_id,
'agent':keys.agent,
'client':keys.point_of_contact,
'company':keys.company,
'service':keys.services
}
form = NewDealForm(request.POST or None,initial = form_dict)
return render(request,
"account/close_lead.html",
{'form':form})
'''
As you can see, I create an object dictionary because the forms are different, so they share some common values not all, and then I simply adapt the dictionary, nice and easy, but I somehow expected that Django somehow finds relationships by name?, but maybe the batteries are not included for this.
How do I update a non existing related object through Django model forms ?
I have two objects: Participant and Emergency. Emergency is a child of participant like if run the query: participant = ParticipantInfo.objects.get(pk = prk) I can access emergency = participant.emergency.
I cannot update emergency with data from a form using a POST request.
Can anyone help me please.
Thanks
Here's my models.py for clarity.
models.py
class EmergencyInfo(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
phone_number = models.CharField(max_length=50)
email = models.EmailField(max_length=100, blank=True, verbose_name="Emergency Contact Email")
relationship = models.CharField(max_length=100)
class ParticipantInfo(models.Model):
first_name = models.CharField(max_length=100)
middle_initial = models.CharField(max_length=1, blank=True)
emergency = models.ForeignKey(EmergencyInfo, on_delete = models.CASCADE, editable= False, null=True, blank=True)
views.py
def update_participant(request, pk):
# this function comes after update_specific
if request.method == "GET":
forms = get_participant_form_data(pk)
context = {'forms': forms, 'pk': pk}
return render(request, 'core/participantinfo_update_form.html', context)
if request.method == "POST":
return update_specific_form(request, pk)
def update_specific_form(request, pk):
participant = ParticipantInfo.objects.get(pk = pk)
# if the object didn't exist create it like normal
if participant.emergency is None:
emergencyform =EmergencyForm(request.POST)
if (emergencyform.is_valid):
emergencyform.save()
messages.success(request, 'saved')
return redirect(request.path_info)
# if the object exists, update it
if participant.emergency is not None:
emergencyform = EmergencyForm(request.POST, instance = participant.emergency)
if (emergencyform.is_valid):
emergencyform.save()
messages.success(request, 'saved')
return redirect(request.path_info)
Your problem seems to be with the is_valid method. Replace it with is_valid(). So your lines would be:
if (emergencyform.is_valid()):
#code
I have found an answer. Not only I needed to call the is_valid() instead of is_valid
but I also needed to tie the created object with it's parent like:
Save the new child object
Save the parent objects (updates the null
foreign key to a value)
*#get the existing parent object*
participant = ParticipantInfo.objects.get(pk = pk)
*#if no child object exists*
if participant.emergency is None:
emergencyform =EmergencyForm(request.POST)
if (emergencyform.is_valid()):
emergency = emergencyform.save(commit=False)
participant.emergency = emergency
emergencyform.save()
participant.save()
messages.success(request, 'saved')
return redirect(request.path_info)
I'm trying to make a view where the user can edit DB records through a form in a template. I've searched a lot of web pages (and Django docs as well) where they teach how to make these views, but they always use the "id" that Django generates for each Model. In this particular Model, I have to use an AutoField to override the "id". Is there a way to use this AutoField as an "id" of the record with Django?
Here's my complete model:
class T031003 (models.Model):
C003IDCD = AutoField(primary_key=True)
C003INST = models.IntegerField(unique=True) #usar AutoSlug
C003TPCD = models.CharField(max_length=1)
C003CHCD = models.CharField(max_length=14)
C003MTR = models.CharField(max_length=30, blank=True, null=True)
C003CTCD = models.CharField(max_length=3)
C003RZSC = models.CharField(max_length=60, blank=True, null=True)
C003EML = models.EmailField(max_length = 254, blank=True, null=True)
C003LOGA = models.CharField(max_length=20)
C003LOGB = models.DateTimeField()
C003LOGD = models.CharField(max_length=15, blank=True, null=True)
C003LOGF = models.CharField(max_length=20, blank=True, null=True)
def __unicode__(self):
return '%s' % self.C003MTR
class T031003Form(ModelForm):
class Meta:
model = T031003
ordering = ["-C003MTR"]
exclude = ('C003LOGA','C003LOGB','C003LOGD','C003LOGE','C003LOGF')
And here's the view I tried to do, but it gives me the error "No T031003 matches the given query." and it's right, since there is no "id" in the table:
def t031003form_edit(request, id=None):
pin = get_object_or_404(T031003, pk=id)
form = T031003Form(request.POST or None, instance=pin)
if request.method == 'POST':
if form.is_valid():
form = form.save(False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
form.save()
form = T031003Form()
else:
return HttpResponseRedirect('/erro/')
return render_to_response('T031003Form_edit.html', {'form': form,}, context_instance=RequestContext(request))
Any help would be very appreciated!
If a model has an AutoField — an auto-incrementing primary key — then that auto-incremented value will be calculated and saved as an attribute on your object the first time you call save():
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
There's no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
ref : https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs
Well, thanks to the help from a close friend, I could do the trick using formsets. Here's the view:
def t031002form_edit(request, id_auto):
j = get_object_or_404(T031002, pk=id_auto)
T031003FormSet = modelformset_factory(T031002, can_delete=True, max_num=1)
if request.method == 'POST':
form = T031002FormSet(request.POST or None, request.FILES or None, queryset=T031002.objects.filter(pk=id_auto))
if form.is_valid():
instance = form.save(commit=False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
for reform in instance:
reform.save()
else:
return HttpResponseRedirect('/erro/')
else:
form = T031002FormSet(queryset=T031002.objects.filter(pk=id_auto))
return render_to_response(('T031002Form_edit.html'), {'form': form,}, context_instance=RequestContext(request))
So, with formsets, you can work nicely and with no worries. Hope it helps others with this same questioning.