Timestamp many2many? - django

I have the following Abstract:
class A:
clientname ...
logs = models.ManyToManyField(B, blank=True, null=True)
class B.
message ...
timestamp = models.DateTimeField()
def save(self, *args, **kwargs):
self.timestamp = datetime.now()
super(B, self).save(*args, **kwargs)
Now, if a message is saved I want it to have a timestamp, always. And I do not want to change the many2many relationship. Is this possible? And if It is how would I go about writing it?
UPDATE:
my view
def log(request):
if request.method == 'POST':
log, created = B.objects.get_or_create(message=request.POST['message'])
client = \
A.objects.get(clientname=request.POST['clientname'])
client.logs.add(log)
return HttpResponse(content="OK", mimetype="text/plain", status=200)
else:
return HttpResponse(content="Failed", mimetype="text/plain", status=400)
RESOLUTON:
I did it, this is kind of a workaround:
def log(request):
if request.method == 'POST':
time = datetime.now()
log, created = \
Log.objects.get_or_create(message=request.POST['message'], \
timestamp=time)
client = \
Thinclient.objects.get(hostname=request.POST['clientname'])
client.logs.add(log)
return HttpResponse(content="OK", mimetype="text/plain", status=200)
else:
return HttpResponse(content="Failed", mimetype="text/plain", status=400)
UPDATE:
No actually that wasn't what I wanted either because this will create new message instances even if the message is the same

You can use the DateTimeField with auto_now argument:
timestamp = DateTimeField(auto_now=True)
More about this:
https://docs.djangoproject.com/en/1.3/ref/models/fields/#django.db.models.DateField.auto_now

add save() to the class B
def save(self, *args, **kwargs):
self.timestamp = datetime.now()
super(B, self).save(*args, **kwargs)

Related

What is the most effective validation method on Flask?

First of all, I try to make an Rest-API with Flask MongoEngine. I create a model which is;
class Project(db.Document):
name = db.StringField(validation=validate_project_name)
created_at = db.DateTimeField()
updated_at = db.DateTimeField()
def save(self, *args, **kwargs):
if not self.created_at:
self.created_at = datetime.datetime.now()
self.updated_at = datetime.datetime.now()
return super(Project, self).save(*args, **kwargs)
def __str__(self):
return self.name
and I also created a Form ;
ProjectForm = model_form(Project, exclude=["created_at", "updated_at"])
and also the custom validation is ;
def validate_project_name(name, min=5, max=25):
if len(name) <= min or len(name) >= max:
raise ValidationError(
f"Proje adı en az {min} en fazla {max} karakter olabilir!"
)
return True
I want to validate with ProjectFrom when i send the data which I get from request body that's why i prepare that code ;
#project.route("/create", methods=["POST"])
def create_project():
data = MultiDict(request.json)
form = ProjectForm(data, meta={"csrf": False})
if form.validate():
if Project(name=form.data["name"]).save():
## will take create action
return True
else:
return Response({form.errors}, status=400)
It returns true when i request but when i change de data on request.body it still returns true where do i mistake can someone help me and explain the logic. Or Should i create a middleware for the validation also i tried that. and i created something. But i don't know is it the best way.
Thanks in advance...
Middleware that i tried to validate data;
def creation_project_validation(func):
#wraps(func)
def decorated_function(*args, **kwargs):
if request.method == "POST":
data = request.json
if data.get("name") is None:
return Response({"Name is required field!"}, status=400)
if validate_project_name(data.get("name")):
return Response(
{"Karakter uzunluğu istenilen şekilde değil!"}, status=400
)
return func(*args, **kwargs)
else:
Response("Method not allowed", status=405)
return decorated_function

mail chimp subscription not working with django

Hi everyone I integrate my Django Web app with mail chimp . in my admin panel when I open marketing preference it give me error . and it do not subscribe my users when I click on subscribe my user remain unsubscribe when i hit save .the error I got is
{'type': 'https://mailchimp.com/developer/marketing/docs/errors/', 'title': 'Invalid Resource', 'status': 400, 'detail': "The resource submitted could not be validated. For field-specific details, see the 'errors' array.", 'instance': '2b647b4f-6e58-439f-8c91-31a3223600a9', 'errors': [{'field': 'email_address', 'message': 'This value should not be blank.'}]}
my models.py file is:
class MarketingPreference(models.Model):
user =models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
subscribed =models.BooleanField(default=True)
mailchimp_subscribed = models.NullBooleanField(blank=True)
mailchimp_msg =models.TextField(null=True , blank=True)
timestamp =models.DateTimeField(auto_now_add=True)
updated =models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.email
def marketing_pref_create_reciever(sender, instance, created, *args, **kwargs):
if created:
status_code, response_data = Mailchimp().subscribe(instance.user.email)
print(status_code, response_data)
post_save.connect(marketing_pref_create_reciever, sender=MarketingPreference)
def marketing_pref_update_reciever(sender, instance, *args, **kwargs):
if instance.subscribed != instance.mailchimp_subscribed:
if instance.subscribed:
#subscribing user
status_code, response_data = Mailchimp().subscribe(instance.user.email)
else:
#unsubscribing user
status_code, response_data = Mailchimp().unsubscribe(instance.user.email)
if response_data['status'] =='subscribed':
instance.subscribed = True
instance.mailchimp_subscribed = True
instance.mailchimp_msg = response_data
else:
instance.subscribed = False
instance.mailchimp_subscribed = False
instance.mailchimp_msg = response_data
pre_save.connect(marketing_pref_update_reciever, sender=MarketingPreference)
def make_marketing_pref_reciever(sender, instance, created, *args, **kwargs):
if created:
MarketingPreference.objects.get_or_create(user=instance)
post_save.connect(make_marketing_pref_reciever , sender=settings.AUTH_USER_MODEL)
my utils.py is:
MAILCHIMP_API_KEY = getattr(settings, "MAILCHIMP_API_KEY" , None)
MAILCHIMP_DATA_CENTER = getattr(settings, "MAILCHIMP_DATA_CENTER" , None)
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)
def check_email(email):
if not re.match(r".+#.+\..+",email):
raise ValueError("String passed is not a valid email address")
return email
def get_subscriber_hash(member_email):
#check email
check_email(member_email)
member_email = member_email.lower().encode()
m = hashlib.md5(member_email)
return m.hexdigest()
class Mailchimp(object):
def __init__(self):
super(Mailchimp, self).__init__()
self.key = MAILCHIMP_API_KEY
self.api_url = "https://{dc}.api.mailchimp.com/3.0/".format(dc=MAILCHIMP_DATA_CENTER)
self.list_id = MAILCHIMP_EMAIL_LIST_ID
self.list_endpoint = '{api_url}/lists/{list_id}'.format(api_url=self.api_url, list_id=self.list_id)
def get_members_endpoint(self):
return self.list_endpoint + "/members"
def change_subscription_status(self, email, status='unsubscribed'):
hashed_email = get_subscriber_hash(email)
endpoint = self.get_members_endpoint() +"/" + hashed_email
data = {
"status":self.check_valid_status(status)
}
r = requests.put(endpoint, auth=("",self.key), data=json.dumps(data))
return r.status_code, r.json()
def check_subscription_status(self,email):
hashed_email = get_subscriber_hash(email)
endpoint = self.get_members_endpoint() +"/" + hashed_email
r = requests.get(endpoint, auth=("", self.key))
return r.status_code, r.json()
def check_valid_status(self, status):
choices = ['subscribed' , 'unsubscribed', 'cleaned' , 'pending']
if status not in choices:
raise ValueError("not a valid choice for email status")
return status
def add_email(self,email):
status = "subscribed"
self.check_valid_status(status)
data = {
"email_address":email,
"status": status
}
endpoint = self.get_members_endpoint()
r = requests.post(endpoint, auth=("",self.key), data=json.dumps(data))
return self.change_subscription_status(email, status='subscribed')
def unsubscribe(self, email):
return self.change_subscription_status(email, status='unsubscribed')
def subscribe(self, email):
return self.change_subscription_status(email, status='subscribed')
def pending(self, email):
return self.change_subscription_status(email, status='pending')
mixins.py is:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
class CsrfExemptMixin(object):
#method_decorator(csrf_exempt)
def dispatch(self, request,*args, **kwargs):
return super(CsrfExemptMixin, self).dispatch(request,*args, **kwargs)
and my views.py is:
from .mixins import CsrfExemptMixin
from .models import MarketingPreference
from . utils import Mailchimp
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)
# Create your views here.
class MarketingPreferenceUpdateView(SuccessMessageMixin, UpdateView):
form_class = MarketingPreferenceForm
template_name = 'base/forms.html'
success_url = '/settings/email/' #this is a builtin method and by default it will go to marketig preference
success_message = 'Your email preferences have been updated. Thank you'
def dispatch(self, *args, **kwargs): #when user came with incognito email will not show to him it will redirect him back to login page
user = self.request.user
if not user.is_authenticated:
return redirect("/login/?next=/settings/email/") #HttpResponse("not allowed", status=400)
return super(MarketingPreferenceUpdateView, self).dispatch(*args,**kwargs)#(request, *args...)
def get_context_data(self, *args, **kwargs):
context = super(MarketingPreferenceUpdateView, self).get_context_data(*args,**kwargs)
context['title'] = 'Update Email Preference'
return context
def get_object(self):
user = self.request.user
obj , created = MarketingPreference.objects.get_or_create(user=user)
return obj
class MailchimpWebhookView(CsrfExemptMixin,View): #it will not work because our web application is not deployes yet and webhook mailchimp do not work with local host
#def get(self, *args, **kwargs):
# return HttpResponse('thank you', status=200)
def post(self, request, *args, **kwargs):
data = request.POST
list_id = data.get('data[list_id]')
if str(list_id) == str(MAILCHIMP_EMAIL_LIST_ID): # I CHECK THAT DATA DATA IS THE RIGHT LIST
hook_type = data.get("type")
email = data.get('data[email]')
response_status, response = Mailchimp().check_subscription_status(email)
sub_status = response['status']
is_subbed = None
mailchimp_subbed = None
if sub_status == "subscribed":
is_subbed, mailchimp_subbed = (True, True)
elif sub_status == "unsubscribed":
is_subbed, mailchimp_subbed = (False, False)
if is_subbed is not None and mailchimp_subbed is not None:
qs = MarketingPreference.objects.filter(user__email__iexact=email)
if qs.exists():
qs.update(subscribed=is_subbed, mailchimp_subscribed=mailchimp_subbed, mailchimp_msg=str(data))
return HttpResponse('thank you', status=200)
Here you have already assigned request.POST to data which is now a dictonary, so to get a value from dictonary you should use the field name of the form widget, as data == request.POST now.
Problem is you are getting wrong key.So your email will always be empty
list_id = data.get('data[list_id]')
email = data.get('data[email]')#You are getting wrong key
It should be like this
list_id = data.get('list_id')
email = data.get('email')

django - logging edit event - need to get id of logged in user

I've a product-stock model as given below.
TRANSACTION_TYPE=(('I','Stock In'),('O','Stock Out'))
class Stock(models.Model):
product=models.ForeignKey('product.Product', blank=False,null=False)
date=models.DateField(blank=False, null=False,)
quantity=models.PositiveIntegerField(blank=False, null=False)
ttype=models.CharField(max_length=1,verbose_name="Transaction type",choices=TRANSACTION_TYPE, blank=False, db_index=True)
I need to log the update activity on this model, along with the id of the user who updated it.
ACTIONS=(('EC','Edit Category'),
('EG','Edit Group'),
('EP','Edit Product'),
('ES','Edit Stock'))
class MyLog(models.Model):
user=models.ForeignKey(auth.models.User, blank=False)
action= models.CharField(max_length=2, choices=ACTIONS, null=False,blank=False)
date=models.DateTimeField(blank=False, auto_now=True)
data = JSONField()
I've the added following code to the Stock model.
def __init__(self, *args, **kwargs):
super(Stock, self).__init__(*args, **kwargs)
if self.pk != None :
self.__important_fields = ['product','date', 'quantity', 'ttype', ]
for field in self.__important_fields:
setattr(self, '__original_%s' % field, getattr(self, field))
field_name='__original_%s' % field
def save(self, *args, **kwargs):
if self.pk != None :
print("Editing")
flag=False
log=MyLog(user=?,action='ES')
log.data=[]
for field in self.__important_fields:
original=getattr(self, '__original_%s' % field)
if original != getattr(self, field):
flag=True
log.data.append({field : str(original)})
if flag:
log.save()
else:
print("Adding")
super(Stock, self).save(*args, **kwargs)
This works, when I hard code a user object into the line log=MyLog(user=?,action='ES').
I need to log the id of the user who performed this edit operation.
How can I achieve this?
Thanks.
Here's how I finally achieved my goal.
Instead of logging the event from the model, I switched my code to the forms.
Here's my final code.
mylog app model
ACTIONS=(('EC','Edit Category'),
('EG','Edit Group'),
('EP','Edit Product'),
('ES','Edit Stock'))
class MyLog(models.Model):
user=models.ForeignKey(settings.AUTH_USER_MODEL, blank=False)
model_id=models.IntegerField(default=0)
action= models.CharField(max_length=2, choices=ACTIONS, null=False,blank=False)
date=models.DateTimeField(blank=False, auto_now=True)
old_data = JSONField(default=None)
new_data = JSONField(default=None)
stock app - update view
class UpdateStock(UpdateView):
model=Stock
form_class=UpdateStockForm
def get_form_kwargs(self):
kwargs = super( UpdateStock, self).get_form_kwargs()
kwargs.update({'user_id': self.request.user.id})
return kwargs
stock app - update form
class UpdateStockForm(forms.ModelForm):
def __init__(self,pk= None, *args, **kwargs):
self.user_id=kwargs.pop('user_id')
super(UpdateStockForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(UpdateStockForm, self).clean()
quantity = cleaned_data.get('quantity')
date= cleaned_data.get('date')
priv_quantity=self.instance.quantity
priv_date=self.instance.date
if priv_quantity!=quantity or priv_date != date:
#log!
log=MyLog(user=auth.models.User.objects.get(pk=self.user_id),action='ES', model_id=self.instance.id)
log.old_data=[]
log.old_data.append({'date' : str(priv_date), 'quantity':priv_quantity })
log.new_data=[]
log.new_data.append({ 'date' : str(date), 'quantity':quantity })
log.save()
return cleaned_data

Django: request.user in form

How may I get the user details to use within a from? I know in the view I can just do:
currentUser=request.user
But if I use it in the form as so I get the following error "'request' is not defined".
class SelectTwoTeams(BootstrapForm):
currentUser=request.user
date_joined = currentUser.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
currentCharities = forms.ModelChoiceField(queryset=Charity.objects.filter(enabled=1), empty_label=None, widget=forms.Select(attrs={"class":"select-format"}))
team1 = forms.ModelChoiceField(queryset=StraightredTeam.objects.none(), empty_label=None,
widget=forms.Select(attrs={"class":"select-format"}))
team2 = forms.ModelChoiceField(queryset=StraightredTeam.objects.none(), empty_label=None,
widget=forms.Select(attrs={"class":"select-format"}))
Many thanks for any help in advance.
Below shows the init of this form just incase it may help. I know how to get access to the user data using kwargs for this part:
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
self.currentSelectedTeam1 = kwargs.pop('currentSelectedTeam1', None)
self.currentSelectedTeam2 = kwargs.pop('currentSelectedTeam2', None)
self.currentfixturematchday = kwargs.pop('currentfixturematchday', None)
self.currentCampaignNo = kwargs.pop('currentCampaignNo', None)
super(SelectTwoTeams, self).__init__(*args, **kwargs)
cantSelectTeams = UserSelection.objects.select_related().filter(~Q(fixtureid__fixturematchday=self.currentfixturematchday),campaignno=self.currentCampaignNo, )
if not cantSelectTeams:
queryset = StraightredTeam.objects.filter(currentteam = 1).order_by('teamname')
else:
queryset = StraightredTeam.objects.filter(currentteam = 1).exclude(teamid__in=cantSelectTeams.values_list('teamselectionid', flat=True)).order_by('teamname')
teamsAlreadyPlaying = StraightredFixture.objects.filter(soccerseason=1025, fixturematchday=self.currentfixturematchday, fixturedate__lte = timezone.now())
postponedGames = StraightredFixture.objects.filter(soccerseason=1025, fixturematchday=self.currentfixturematchday,fixturestatus = "P")
queryset = queryset.exclude(teamid__in=teamsAlreadyPlaying.values_list('home_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=teamsAlreadyPlaying.values_list('away_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=postponedGames.values_list('home_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=postponedGames.values_list('away_team_id', flat=True)).order_by('teamname')
self.fields['team1'].queryset = queryset
self.fields['team2'].queryset = queryset
self.fields['team1'].initial = self.currentSelectedTeam1
self.fields['team2'].initial = self.currentSelectedTeam2
self.fields['team1'].label = False
self.fields['team2'].label = False
date_joined = user.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
self.fields['currentCharities'].label = False
The form class is defined when the module is loaded. That means that you can't set currentUser = request.user, since you don't have access to the request object yet. You should remove that line from your code.
The correct approach is to override the __init__ method so that it takes the user. If your field definitions depend on the user then you need to move them into the __init__ method.
class SelectTwoTeams(BootstrapForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(SelectTwoTeams, self).__init__(*args, **kwargs)
date_joined = self.user.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
self.fields['currentCharities'] = forms.ModelChoiceField(queryset=Charity.objects.filter(enabled=1))
...
You should only use None as the default when popping if the user is not required. It is required in your case, since you access self.user.date_joined in the __init__ method. By storing the user as self.user, you can access it in other methods if required.
Finally, you need to change your view to pass the user when you instantiate the form.
if request.method == "POST"
form = SelectTwoTeams(request.POST, user=request.user)
...
else:
form = SelectTwoTeams(user=request.user)
You can overwrite the save method and send the request there.
viewys.py
if request.method == "POST"
if forms.is_valid():
form.save(request=request.user)
and in your forms.py:
def save(self, request=None, *args, **kwargs
self.currentUser = request.user
super(SelectTwoTeams, self).save(*args, **kwargs)
instance.save()
return instance
To get any variable in forms you must pass as kwarg argument then get-it in init
In the Form:
class someForm(forms.ModelForm):
...code...
def __init__(self, *args,**kwargs):
self.Name = kwargs.pop('SomeName')
in your views:
yourform = someForm(request.POST or None, initial={'foo': foo}, SomeName= someVar)

ModelForm and Model validation playing together

I have the following Model :
class Advertisement(models.Model):
slug = models.UUIDField(default=uuid4, blank=True, editable=False)
advertiser = models.ForeignKey(Advertiser)
position = models.SmallIntegerField(choices=POSITION_CHOICES)
share_type = models.CharField(max_length=80)
country = CountryField(countries=MyCountries, default='DE')
postal_code = models.CharField(max_length=8, null=True, blank=True)
date_from = models.DateField()
date_to = models.DateField()
Based on Advertiser, position, type country and postal code this stores adverisements with range date_from and date_to.
advertiser, position, share_type, country and postal_code
are coming from the request and are fetched in
class CreateAdvertisment(LoginRequiredMixin, CreateView):
# Some usefull stuff
def dispatch(self, request, *args, **kwargs):
self.advertiser = Advertiser.objects.get(user=self.request.user)
self.share_type = self.kwargs.get('share_type', None)
self.country = self.kwargs.get('country', None)
self.postal_code = self.kwargs.get('postal_code', None)
self.position = int(self.kwargs.get('position', None))
self.position_verbose = verbose_position(self.position)
ret = super(CreateAdvertisment, self).dispatch(request, *args, **kwargs)
return ret
Without any validation for checking date_from, date_to. I can simply do
def form_valid(self, form):
form.instance.advertiser = self.advertiser
form.instance.share_type = self.share_type
form.instance.country = self.country
form.instance.postal_code = self.postal_code
form.instance.position = self.position
ret = super(CreateAdvertisment, self).form_valid(form)
return ret
and I am done. Unfortunatly I cannot do this as I do have to check for valid time Frames for the Advertisment to avoid Double Bookings of the same time. I do this in the model with the following :
def clean(self):
ret = super(Advertisement, self).clean()
print ("country [%s] position [%s] share_type [%s] postal_code [%s]" % (self.country,
self.position, self.share_type, self.postal_code))
if self.between_conflict():
raise ValidationError("Blocks between timeframe")
elif self.end_conflict():
raise ValidationError("End occupied")
elif self.during_conflict():
raise ValidationError("Time Frame complete occupied")
elif self.start_conflict():
raise ValidationError("Start Occupied")
return ret
def start_conflict(self):
start_conflict = Advertisement.objects.filter(country=self.country,
position=self.position,
share_type=self.share_type,
postal_code=self.postal_code).filter(
date_from__range=(self.date_from, self.date_to))
return start_conflict
This works well and I filter out any Conflict for the given period. Problem is that I do not have the instance variables as they are set in view.form_valid() and model.clean() is called by the form validation process.
I do have an chicken egg problem here. I am thinking about setting the requests parameters to the form kwargs in
def get_form_kwargs(self, **kwargs):
kwargs = super(CreateAdvertisment, self).get_form_kwargs()
kwargs['advertiser'] = self.advertiser
kwargs['position'] = self.position
....
and then putting them into the form instance in form.init()
def __init__(self, *args, **kwargs):
advertiser = kwargs.pop('advertiser')
position = kwargs.pop('position')
# .. and so on
super(AdvertismentCreateForm, self).__init__(*args, **kwargs)
For some reasons I do not think this is very pythonic. Does anybody have a better idea? I will post my solution.
I think that overriding get_form_kwargs is ok. If all the kwargs are instance attributes, then I would update the instance in the get_form_kwargs method. Then you shouldn't have to override the form's __init__, or update the instance's attributes in the form_valid method.
def get_form_kwargs(self, **kwargs):
kwargs = super(CreateAdvertisment, self).get_form_kwargs()
if kwargs['instance'] is None:
kwargs['instance'] = Advertisement()
kwargs['instance'].advertiser = self.advertiser
...
return kwargs
In the model's clean method, you can now access self.advertiser.
alasdairs proposal works fine I have the following now :
def get_form_kwargs(self, **kwargs):
kwargs = super(CreateAdvertisment, self).get_form_kwargs()
if kwargs['instance'] is None:
kwargs['instance'] = Advertisement()
kwargs['instance'].advertiser = self.advertiser
kwargs['instance'].share_type = self.share_type
kwargs['instance'].country = self.country
kwargs['instance'].postal_code = self.postal_code
kwargs['instance'].position = self.position
return kwargs
def form_valid(self, form):
ret = super(CreateAdvertisment, self).form_valid(form)
return ret
Of course there is no need to override form_valid anymore. I have just included here in order to display that we do not set the instance fields anymore as this is done in get_form_kwargs() already