default image for albums
click edit album
update logo url
error if image is not fetch from url
image fetched
album updated
postgres old album table, logo image name is saved
postgres new album table, logo image itself is saved in base64 string
#django/models
class Album(models.Model):
artist = models.CharField(max_length=50)
album_title = models.CharField(max_length=50)
genre = models.CharField(max_length=50)
album_logo = models.TextField(default='iVBORw0KGgoAAAANS...')
date_posted = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
price = models.DecimalField(decimal_places=2,max_digits=5, default=10)
-------------------------------------
#django/api/serializers
class MusicSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = '__all__'
#serializer doesn't have text field, has to change to charfield instead
def __init__(self, *args, **kwargs):
super(MusicSerializer, self).__init__(*args, **kwargs)
self.fields['album_logo'] = serializers.CharField(required=True)
----------------------------------
#django/api/apiview
class AlbumList(APIView):
def get(self, request, format=None):
username = request.GET.get('author')
data_length = request.GET.get('data_length')
#filter by author
if username==None:
albums = Album.objects.all()
else:
author_id = get_object_or_404(User, username=username).pk
albums = Album.objects.filter(author=author_id).order_by('-date_posted')
#filter by data length
if data_length!=None:
try:
int(data_length)
except ValueError:
return Response('data length is invvalid', status=status.HTTP_406_NOT_ACCEPTABLE)
else:
albums = albums[:int(data_length)]
serializer = MusicSerializer(albums, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = MusicSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class AlbumDetail(APIView):
def get_object(self, pk):
try:
return Album.objects.get(pk=pk)
except Album.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
album = self.get_object(pk)
serializer = MusicSerializer(album)
return Response(serializer.data)
def put(self, request, pk, format=None):
album = self.get_object(pk)
#only owner can edit
if album.author != request.user:
return Response({"detail": "You do not have permission to perform this action."},
status= status.HTTP_403_FORBIDDEN)
serializer = MusicSerializer(album, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
album = self.get_object(pk)
# only owner can delete
if album.author != request.user:
return Response({"detail": "You do not have permission to perform this action."},
status=status.HTTP_403_FORBIDDEN)
album.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
---------------------------------
#django/api/urls
path('album_list/', apiview.AlbumList.as_view(), name='AlbumList'),
path('album_detail/<int:pk>/', apiview.AlbumDetail.as_view(), name='AlbumDetail'),
----------------------------------
//cmd
npm install base64-to-image
------------------------------------
//react/pages/albumForm
import React, { Component } from 'react';
import '../App.css';
import { Input } from 'antd';
import { FaOpencart, FaLyft, FaSignature, FaDollarSign, FaRegShareSquare } from "react-icons/fa";
import { Button } from 'reactstrap';
import { connect } from 'react-redux';
import { postAlbum } from '../redux/actions/postAlbum';
import { putAlbum } from '../redux/actions/putAlbum';
import { Redirect } from 'react-router-dom';
import { getAlbums } from '../redux/actions/getAlbums';
import { getUsers } from '../redux/actions/getUser';
import { message } from 'antd';
const image2base64 = require('image-to-base64');
class albumForm extends Component {
constructor(props) {
super(props);
this.state = {
artist: '',
album_title: '',
genre: '',
album_logo: null,
mode: 'add',
price: 0,
imgSize: null,
};
}
componentDidMount() {
if (this.props.loggedin) {
if (!this.props.gotUsers) {
this.props.dispatch(getUsers(this.props.token));
}
if (!this.props.gotAlbums) {
this.props.dispatch(getAlbums(this.props.token));
}
}
//detecting if add or update
const pathName = window.location.pathname.split('/');
const albumId = pathName[pathName.length - 1];
if (albumId == 'addAlbum') {
//add
this.setState({ mode: 'add' })
} else {
//update
const album_id = parseInt(albumId);
const waitGetAlbums = setInterval(() => {
if (this.props.gotAlbums) {
const album = this.props.albums.filter(album => album.id == album_id)[0];
this.setState({
mode: 'update',
artist: album.artist,
album_title: album.album_title,
genre: album.genre,
album_logo: album.album_logo,
price: album.price,
})
clearInterval(waitGetAlbums);
}
}, 100)
}
}
inputChange = (e, p) => {
//album_logo save img in base64
if (p === 'album_logo') {
image2base64(e.target.value) // you can also to use url
.then(
(response) => {
this.setState({ imgSize: response.length, album_logo: response }) //cGF0aC90by9maWxlLmpwZw==
}
)
.catch(
(error) => {
message.error(error.toString()) //Exepection error....
}
)
}
else {
this.setState({ [p]: e.target.value });
}
}
fileChange = (e) => {
console.log(e.target.files);
this.setState({ album_logo: e.target.files[0] })
}
formSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.set('artist', this.state.artist);
formData.set('album_title', this.state.album_title);
formData.set('genre', this.state.genre);
formData.set('author', this.props.users.filter(user => user.username == this.props.username)[0].id);
formData.set('album_logo', this.state.album_logo);
formData.set('price', this.state.price);
if (this.state.mode == 'add') {
this.props.dispatch(postAlbum(this.props.token, formData))
//wait for 5sec, check every sec to see if post successful
let i = 0;
const waitPost = setInterval(() => {
if (this.props.added) {
message.success('post successful')
clearInterval(waitPost);
}
if (i == 50) {
message.error('post timeout')
clearInterval(waitPost);
}
i++;
}, 100)
}
else {
//update
const pathName = window.location.pathname.split('/');
const albumId = parseInt(pathName[pathName.length - 1]);
this.props.dispatch(putAlbum(this.props.token, albumId, formData));
let i = 0;
const waitPost = setInterval(() => {
if (this.props.updated) {
message.success('post updated')
clearInterval(waitPost);
}
if (i == 50) {
message.error('update timeout')
clearInterval(waitPost);
}
i++;
}, 100)
}
}
render() {
if (!this.props.loggedin) {
return <Redirect to='/login' />
}
return (
<form style={{ marginLeft: '10px', width: '300px' }}
onSubmit={(e) => this.formSubmit(e)}>
<legend>Album Form</legend>
<hr />
<p style={{ color: 'red' }}>{this.props.error}</p>
<Input placeholder={this.state.mode == 'add' ? "artist" : this.state.artist}
required={this.state.mode == 'add' ? true : false}
prefix={<FaOpencart style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => this.inputChange(e, 'artist')}
style={{ marginTop: '5px' }}
/>
<Input placeholder={this.state.mode == 'add' ? "album title" : this.state.album_title}
required={this.state.mode == 'add' ? true : false}
prefix={<FaSignature style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => this.inputChange(e, 'album_title')}
style={{ marginTop: '15px' }}
/>
<Input placeholder={this.state.mode == 'add' ? "genre" : this.state.genre}
required={this.state.mode == 'add' ? true : false}
prefix={<FaLyft style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => this.inputChange(e, 'genre')}
style={{ marginTop: '15px' }}
/>
<Input type='number' placeholder={this.state.mode == 'add' ? "price" : this.state.price}
required={this.state.mode == 'add' ? true : false}
prefix={<FaDollarSign style={{ color: 'rgba(0,0,0,.25)' }} />}
onChange={(e) => this.inputChange(e, 'price')}
style={{ marginTop: '15px' }}
/>
<Input type='url' placeholder={this.state.mode == 'add' ? "logo url(https://example.com/img.png)" : 'https://example.com/img.png'}
required={this.state.mode == 'add' ? true : false}
prefix={<FaRegShareSquare style={{ color: 'rgba(0,0,0,.25)' }} />}
onBlur={(e) => this.inputChange(e, 'album_logo')}
style={{ marginTop: '15px' }}
/>
{this.state.album_logo ? <div style={{ marginTop: '15px' }}>
<img src={`data:image/jpeg;base64,${this.state.album_logo}`}
style={{ height: '75px', width: '100px' }} />
{this.state.imgSize ? <span style={{ marginLeft: '10px' }}>size: {this.state.imgSize} byte</span> : null}
</div>
: null}
<Button color="success" type='submit' size='sm'
style={{ marginTop: '15px' }}
>Submit</Button>
</form>
);
}
}
export default connect(
(store) => {
return {
token: store.login.token,
loggedin: store.login.fetched,
added: store.albums.added,
gotAlbums: store.albums.fetched,
albums: store.albums.albums,
gotUsers: store.users.fetched,
users: store.users.users,
username: store.login.username,
error: store.albums.error,
updated: store.albums.updated,
};
}
)(albumForm);
reference:
http://chuanshuoge2.blogspot.com/2019/08/django-64-saveread-images-in-base64.html
No comments:
Post a Comment