When I run this, I get JSON file but the foreign key (contact numbers) are not included, I want to display one contact name/address/email with multiple contact numbers.
models.py
from django.db import models
class PhoneBook(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=100, default='address')
email = models.CharField(max_length=50, default='email')
note = models.CharField(max_length=100, default='note')
def __str__(self):
return self.name
class ContactNumber(models.Model):
number = models.ForeignKey(PhoneBook, related_name="contact_numbers")
contact_number= models.CharField(max_length=30)
def __str__(self):
return self.contact_number
views.py
from django.shortcuts import render
from .models import PhoneBook,ContactNumber
from django.http import JsonResponse
from django.views import View
class PhoneBookList(View):
def get(self,request):
phonebooklist=list(PhoneBook.objects.values())
return JsonResponse(phonebooklist,safe=False)
admin.py
from django.contrib import admin
from .models import PhoneBook,ContactNumber
class ContactNumberInline(admin.StackedInline):
model = ContactNumber
class PhoneBookAdmin(admin.ModelAdmin):
inlines =[
ContactNumberInline,
]
admin.site.register(PhoneBook)
admin.site.register(ContactNumber)
RESULT:
enter image description here
I might be able to answer with this approach.
class PhoneBook(models.Model):
....
def to_json(self):
contact_numbers = [c.contact_number
for c in self.contact_numbers.all()]
return {
'name': self.name,
'email': self.email,
'address': self.address,
'note': self.note,
'contact_numbers': contact_numbers
}
In your view.
class PhoneBookList(View):
def get(self,request):
phonebooklist = PhoneBook.objects.all()
serialized_data = [pb.to_json() for pb in phonebooklist]
return JsonResponse(serialized_data, safe=False)
A bit dirty solution though
Related
I have 2 models named Recipe and Step..
I have serialized both to make an API for GET request.. I want to know is there a way to create for POST request so that I can send both the data (steps and recipe) in the same request?
models.py:
from django.db import models
class Recipe(models.Model):
title = models.CharField( max_length=50)
uuid = models.CharField( max_length=100)
def __str__(self):
return f'{self.uuid}'
class Step(models.Model):
step = models.CharField(max_length=300)
uuid = models.ForeignKey(Recipe, on_delete=models.CASCADE)
def __str__(self):
return f'{self.step} - {self.uuid}'
serializers.py:
from rest_framework import serializers
from .models import *
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ['title', 'uuid']
class StepSerializer(serializers.ModelSerializer):
class Meta:
model = Step
fields = ['step', 'uuid']
views.py:
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import *
from .models import *
#api_view(['GET'])
def apiOverview(request):
api_urls = {
'List':'/recipe-list/',
'Detail View':'/recipe-detail/<str:pk>/',
'Create':'/recipe-create/',
'Update':'/recipe-update/<str:pk>/',
'Delete':'/recipe-delete/<str:pk>/',
'Steps' : '/steps/<str:pk>'
}
return Response(api_urls)
#api_view(['GET'])
def recipeList(request):
recipes = Recipe.objects.all()
serializer = RecipeSerializer(recipes, many=True)
return Response(serializer.data)
#api_view(['GET'])
def recipeDetail(request, pk):
recipe = Recipe.objects.get(uuid=pk)
recipe_serializer = RecipeSerializer(recipe, many=False)
steps = Step.objects.filter(uuid=pk)
steps_serializer = StepSerializer(steps, many=True)
return Response({
'recipe' : recipe_serializer.data,
'steps' : steps_serializer.data
})
How can I create a view for POST and handle both the models?
Try:
from rest_framework import generics
from .models import *
class StepAndRecipe(generics.CreateAPIView):
queryset = Step.objects.all()
queryset = Recipe.objects.all()
serializer_class = StepSerializer
serializer_class = RecipeSerializer
Add in urls.py:
from django.urls import path
from .views import StepAndRecipe
urlpatterns = [
path('steprecipepost', StepAndRecipe.as_view(), name='steps_recipes')
This will only work with POST! And one more thing: take care with the raw data and the HTML form, maybe theses get a little confused since you are using two models in the same view.
I can upload the code, however it is a very basic form that has an ImageField and a Model with and ImageField, however I can upload any file type. I have installed PIL and I am successfully writing the uploaded files to the media directory but as I stated, they can be whatever I like as no validation appears to be happening.
Update has been added, sorry I didn't do this earlier.
Views.
from django.shortcuts import render
from .forms import QuoteForm, ImageForm
from django.http import HttpResponse
from django.core.mail import EmailMessage
from django.shortcuts import redirect
from django.template.loader import get_template
from .models import Quote, Image, Job
from django.forms import modelformset_factory
from django.contrib import messages
from django.http import HttpResponseRedirect
def job_index(request):
jobs = Job.objects.all()
context = {
'jobs':jobs
}
return render (request, 'about.html', context)
def quote(request):
if request.method == 'POST':
form = QuoteForm(request.POST)
files = request.FILES.getlist('image')
if form.is_valid():
quote_form = form.save(commit=False)
quote_form.save()
messages.success(request, 'Form submission successful')
HttpResponse('image upload success')
form=QuoteForm()
for f in files:
Image.objects.create(quote=quote_form, image = f)
else:
print("Oh Know")
else:
form = QuoteForm()
context ={
'form': form,
}
return render (request, 'quote.html', context)
Form
from django import forms
from .models import Quote, Image
from django.core.exceptions import ValidationError
from django.core import validators
from django.utils.translation import gettext_lazy as _
import csv
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, ButtonHolder, Submit
SERVICES = [
('lawn', 'Lawn Mowing'),
('hedge', 'Hedge Triming'),
]
# def check_size(value):
# if len(value)<6:
# raise ValidationError("You fucked it")
class QuoteForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(attrs={"placeholder": "Post Code to check if we service your area"}))
message = forms.CharField(widget=forms.TextInput(attrs={"placeholder": "Title"}))
date_call = forms.CharField(widget=forms.TextInput(attrs={"id":"datetimes"}))
service = forms.ChoiceField(choices=SERVICES)
class Meta:
model = Quote
fields = ('title', 'message', 'email', 'date_call', 'service')
def clean_title(self):
with open("/Users/Mitch/Desktop/Work/Website/db_landscaping/db_landscaping/static/postcodes.csv", mode = "r") as csvDataFile:
csvReader = csv.reader(csvDataFile)
title = self.cleaned_data.get("title")
for row in csvReader:
if title not in row:
raise ValidationError('Postcode invalid or we do not service your area')
return title
class ImageForm(forms.ModelForm):
image = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta (QuoteForm.Meta):
fields = QuoteForm.Meta.fields + ('image',)
Models
from django.db import models
from django.db.models.signals import pre_save
from db_landscaping import settings
import os
from django.template.defaultfilters import slugify
class Quote (models.Model):
title = models.CharField(null=True, max_length=100)
message = models.TextField(null=True,blank=True)
email = models.EmailField( null=True)
slug = models.SlugField(null=True ,unique=True )
service = models.CharField(max_length=100, null=True)
date_call = models.TextField(null=True)
created_date = models.DateTimeField(null=True, auto_now_add=True)
last_modified = models.DateTimeField(null=True, auto_now=True)
class Job (models.Model):
title = models.CharField(null=True, max_length=100)
message = models.TextField(null=True,blank=True)
image = models.ImageField(upload_to="images/job",null=True,blank=True)
class Image (models.Model):
quote = models.ForeignKey(Quote, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to="images/",null=True,blank=True)
def __str__(self):
return self.quote.title + "Image"
Pretty sure am late here, but maybe in case anyone has the same issue, I think you would have had to create your own validator or use Djangos: Django.core.validators.validate_image_file_extension
and add this to your model field i.e
image = models.ImageField(upload_to="images/",null=True,blank=True, validators=[validate_image_file_extension])
That way, your uploaded files would be validated and incase there not an image, a validation error would be raised.
I have exhausted all avenues in trying to put together a solution for this, but my current knowledge of Python and Django can only get me so far.
I'm creating a basic ticketing system and CreateView used to work until I created a Profile model and then separated the Ticket model into its own app. There were already a couple of tickets created when I refactored my code which is why I know ListView works, DeleteView works as well as DetailView. CreateView works until I hit the save button.
My views and models are below; I hope someone can please help me sort this out.
Ticket Model
from django.db import models
from django.contrib.auth.models import User
....
from qcapp.models import Profile
class Ticket(models.Model):
# Relations
user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="tickets", verbose_name="user")
# Attributes
title = models.CharField(max_length=250, verbose_name="Title", help_text="Enter a Ticket Title")
color = models.CharField(max_length=7,
default="#ffffff",
validators=[RegexValidator("(^#[0-9a-fA-F]{3}$)|(^#[0-9a-fA-F]{6}$)")],
verbose_name="Color",
help_text="Enter the hex color code, like #ccc or #cccccc")
description = models.TextField(max_length=1000)
created_date = models.DateTimeField(default=timezone.now, verbose_name='Created Date')
created_by = models.ForeignKey(User, related_name='created_by_user')
# Attributes
# Object Manager
objects = managers.ProjectManager()
# Meta and String
class Meta:
verbose_name = "Ticket"
verbose_name_plural = "Tickets"
ordering = ("user", "title")
unique_together = ("user", "title")
def __str__(self):
return "%s - %s" % (self.user, self.title)
def get_absolute_url(self):
return reverse('ticket_detail', args=[str(self.id)])
Ticket View (CreateView Only)
# -*- coding: utf-8 -*-
...
from django.views.generic import CreateView, UpdateView, DeleteView
...
from .models import Ticket
...
class TicketCreate(CreateView):
model = Ticket
template_name = "tickets/ticket_form.html"
fields = ['title', 'description']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super(TicketCreate, self).form_valid(form)
...
Profile Model(Imported Into Ticket Model)
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from . import managers
class Profile(models.Model):
# Relations
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile", verbose_name="user")
# Attributes
interaction = models.PositiveIntegerField(default=0, verbose_name="interaction")
# Attributes
# Object Manager
objects = managers.ProfileManager()
# Custom Properties
#property
def username(self):
return self.user.username
# Methods
# Meta and String
class Meta:
verbose_name = "Profile"
verbose_name_plural = "Profiles"
ordering = ("user",)
def __str__(self):
return self.user.username
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
It looks like you need to add the following to your TicketCreate class in the form_valid function:
form.instance.user = Profile.objects.get(user=self.request.user)
Let me know if that works!
I have a video model in django. Currently when a video is saved, the current logged user is not saved in the model (Other fields have a value, except the user field). The form uses a generic create django view.
I would like to know how to save the current logged user when a new video is saved?
multimedia/models.py
from django.db import models
from django.contrib.auth.models import User
from django.forms import ModelForm
from django.core.exceptions import PermissionDenied
from django.db.models.signals import post_save
from django.contrib.auth.decorators import login_required
class Video(models.Model):
user = models.ForeignKey(User, related_name='+',blank=True, null=True)
title = models.CharField(max_length=200)
description = models.TextField()
created = models.DateTimeField('date created', auto_now_add=True, blank=False)
file_url = models.CharField(max_length=2000, blank=True)
file_name = models.CharField(max_length=255, blank=True)
file_uploaded = models.DateTimeField('date file uploaded', null=True, blank=True)
file_upload_speed = models.FloatField(null=True, blank=True)
file_size = models.IntegerField(null=True, blank=True)
def has_file(self):
return len(self.file_url) > 0
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
return ('multimedia_video_detail', [self.id])
class VideoForm(ModelForm):
class Meta:
model = Video
# fields = ('title', 'description')
exclude = ('file_url', 'file_name', 'file_uploaded','file_upload_speed', 'file_size')
multimedia/views.py
from django.conf.urls.defaults import patterns, include, url
from models import Video, VideoForm
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$','django.views.generic.list_detail.object_list',{ 'queryset': Video.objects.all() }, name='multimedia_video_list'),
url(r'^new$', 'django.views.generic.create_update.create_object',{ 'model': Video },name='multimedia_video_new'),
url(r'^(?P<object_id>[\d]*)$', 'django.views.generic.list_detail.object_detail',{ 'queryset': Video.objects.all() }, name='multimedia_video_detail'),
url(r'^(?P<object_id>[\d]*)/edit$','django.views.generic.create_update.update_object',{ 'form_class': VideoForm }, name='multimedia_video_edit'),
url(r'(?P<object_id>[\d]*)/delete$', 'django.views.generic.create_update.delete_object', { 'model': Video, 'post_delete_redirect': '/videos' }, name='multimedia_video_delete'),
url(r'^(?P<object_id>[\d]*)/upload$','multimedia.views.upload_video',name='multimedia_video_upload'),
url(r'^(?P<object_id>[\d]*)/upload/done$','multimedia.views.upload_video_done',name='multimedia_video_upload_done'),
)
Thanks for any help to solve this issue
Instead of directly using the generic creation view, you need to use a wrapper around it that pulls the current user out of the request. Additionally, you create a model form that accepts the user, and overrides the save() method to set the user value on the model instance.
def VideoForm(user):
class _wrapped(forms.ModelForm):
class Meta:
model = models.Video
def save(self, *args, **kwargs):
self.instance.user = user
super(_wrapped, self).save(*args, **kwargs)
return _wrapped
#login_required
def create(request):
return django.views.generic.create_update.create_object(form_class=VideoForm(request.user), name='multimedia_video_new')
I am working on form models and get this error:
global name 'AdForm' is not defined
In my view I have:
from django.template import RequestContext, loader
from django.http import HttpResponse
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
from django import forms
#login_required
def create(request):
if request.POST:
ad = AdForm(request.POST)
if ad.is_valid():
test = 'valid'
else:
test = 'invalid'
else:
test = 'none'
template = loader.get_template('ads/create.html')
context = RequestContext(request, {
'test': test
})
return HttpResponse(template.render(context))
However it is not picking up my model. My model in my view is:
from django.db import models
from django.forms import ModelForm
TYPE_CHOICES = (
'Image',
'Code',
)
SIZE_CHOICES = (
'Leaderboard',
'Banner',
'Skyscraper',
'Square',
)
class Ad(models.Model):
title = models.CharField(max_length=40)
type = models.CharField(max_length=7)
size = models.CharField(max_length=16)
clicks = models.IntegerField()
media = models.ImageField(upload_to='ads')
link = models.URLField(null=True)
created = models.DateTimeField(auto_now_add=True)
expires = models.DateTimeField(null=True)
def __unicode__(self):
return self.name
class AdForm(ModelForm):
class Meta:
model = Ad
Does anyone know why it is not picking up the form model?
Thanks from a noob.
At the top of your view, you need:
from .models import AdForm
Also, forms usually go in forms.py, not with the models.