how to make a char field behave like URLField in django - django

I am using Django and I want a field for URLs I know there is a URLField in Django but I want that field to be a charfield and I don't want users to submit anything other than URL in that charfield
take a look at my models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
email = models.EmailField(max_length=500, blank=True, null=True)
social_github = models.CharField(max_length=200, blank=True, null=True)
social_twitter = models.CharField(max_length=200, blank=True, null=True)
social_linkedin = models.CharField(max_length=200, blank=True, null=True)
social_youtube = models.CharField(max_length=200, blank=True, null=True)
social_website = models.CharField(max_length=200, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(
default=uuid.uuid4, unique=True, primary_key=True, editable=False)
def __str__(self):
return str(self.username)
Please answer how can I make a char field behave like a URL field

An URLField is in essence just a CharField with some validation (and a default name that defaults to (a translation of) URL and a maximum length of 200. Indeed if we look at the source code [GitHub], we see:
class URLField(CharField):
default_validators = [validators.URLValidator()]
description = _("URL")
def __init__(self, verbose_name=None, name=None, **kwargs):
kwargs.setdefault('max_length', 200)
super().__init__(verbose_name, name, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
if kwargs.get("max_length") == 200:
del kwargs['max_length']
return name, path, args, kwargs
def formfield(self, **kwargs):
# As with CharField, this will cause URL validation to be performed
# twice.
return super().formfield(**{
'form_class': forms.URLField,
**kwargs,
})
It also uses a different form field: the URLField is [implemented [GitHub] as):
class URLField(CharField):
widget = URLInput
# …
It will thus use a URLInput widget, but there is nothing that prevents you from using another widget. Indeed, we can implement the model with:
class Profile(models.Model):
# …
social_github = models.URLField(blank=True, null=True)
social_twitter = models.URLField(blank=True, null=True)
social_linkedin = models.URLField(blank=True, null=True)
social_youtube = models.URLField(blank=True, null=True)
social_website = models.URLField(blank=True, null=True)
# …
If you want to change the widget, for example in a ModelForm, you can work with:
class MyProfileForm(forms.ModelForm):
# …
class Meta:
# …
widgets = {
'social_github': forms.TextInput,
'social_twitter': forms.TextInput,
'social_linkedin': forms.TextInput,
'social_youtube': forms.TextInput,
'social_website': forms.TextInput
}

Related

Django Admin, show inline based on slug

Have the following models
class FootballWebsite(models.Model):
"""Football service website."""
url = models.URLField, unique=True)
#football service
id = models.CharField(primary_key=True,
#is this domain blocked
blocked = models.BooleanField(default=False)
#is it online or offline
online = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
sub_categories = models.ForeignKey(SubCategory, default=1)
referral = models.TextField(blank=True)
mirror = models.ForeignKey('FootballWebsite', blank=True, null=True)
rank = models.PositiveIntegerField(default=0, blank=True, null=True)
screenshot = models.BooleanField(default=False)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return u'%s' % (self.url)
"""The datetime when the football service was last seen online"""
try:
return self.footballwebsitestatus_set.filter(online=True).latest('time').time
except FootballWebsiteStatus.DoesNotExist:
return None
class FootballWebsiteDescription(models.Model):
"""Football service website description."""
about = models.ForeignKey(Footballebsite)
title = models.TextField(blank=True, null=True)
keywords = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
relation = models.URLField(blank=True, null=True)
subject = models.TextField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
language = models.TextField(null=True, blank=True)
contactInformation = models.TextField(null=True, blank=True)
officialInfo = models.BooleanField(default=False)
slug = AutoSlugField(populate_from=['title'], allow_duplicates=True, null=True)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return unicode(self.about.url)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(FootballebsiteDescription, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s' % (self.title)
I have a huge amount of links, and i would like to bulk assign them into a category or mark them as blocked based on identical title slug.
Managed to at least get a list of title_slugs with the code below, but the following step, i would like to get an inline list with all sites that have an identical title_slug and bulk assign those all in their a category
class FootballWebsiteInline(admin.TabularInline):
model = FootballWebsite
class FootballWebsiteDescriptionAdmin(admin.ModelAdmin):
list_display = ['show_slug']
def show_slug(self, obj):
return format_html("<a href='{url}'>{url}</a>", url=obj.slug)
inlines = [
FootballWebsiteInline,
]
Above code obviously doesn' t work, since the title slug which can appear many times is not a primary key.
Is it possible to get an inline list based on the title slug in this case which is not a primary key at all, or am i going about this the wrong way?
When possible at all, some further tweaking would be to group the identical title slugs

Django post primary key as slug? How do I reference the primary key in models.py?

When creating the slug for my post model, I want to use the object's primary key as the slug. However when I create a new post, instance.id is NoneType and not an integer like I'd imagine.
Here is my model:
class Post(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=50, null=False, blank=False)
body = models.TextField(max_length=5000, null=False, blank=False)
image = models.ImageField(upload_to=upload_location, null=False, blank=False)
date_published = models.DateTimeField(auto_now_add=True, verbose_name="date published")
date_updated = models.DateTimeField(auto_now=True, verbose_name="date updated")
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.title
#receiver(post_delete, sender=Post)
def submission_delete(sender, instance, **kwargs):
instance.image.delete(False)
def pre_save_blog_post_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = slugify(instance.id)
pre_save.connect(pre_save_blog_post_receiver, sender=Post)
As you can see in pre_save_blog_post_receiver, instance.id returns as None. How do I correctly reference the primary key in this situation?
You need to use post_save in case you create a Post object, since before it is committed to the database, there is no primary key. The database "distributes" primary keys.
Slugifying with an id is however a bit "odd". The idea of a slug is to make a visually pleasant variant of the title or some other text-related attribute.
It might also be more convenient to make use of a AutoSlugField [readthedocs.io] of the django-autoslug [readthedocs.io] package:
from autoslug import AutoSlugField
class Post(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=50)
body = models.TextField(max_length=5000)
image = models.ImageField(upload_to=upload_location)
date_published = models.DateTimeField(auto_now_add=True, verbose_name='date published')
date_updated = models.DateTimeField(auto_now=True, verbose_name='date updated')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(populate_from='title')
def __str__(self):
return self.title

Difficulty adding slug to Generic Detail View in Django

The view works with just the PK, however, the problem is that my PK on the live site is an incoherent string and I want to make it coherent titling for an article to boost SEO. I don't want to change the PK to the slug. I want both.
When I try to add both the PK and a slug it fails and I get the error: no reverse match.
URL path:
path('idea/<slug:slug>,<int:pk>', views.IdeaDetailView.as_view(), name='detail')
Model:
class Idea(models.Model):
idea_id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Searches ID')
idea_number = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
idea_title = models.CharField(max_length=300, blank=True, null=True)
idea_text = NonStrippingTextField(max_length=10000, blank=True, null=True)
Views.py:
class IdeaDetailView(generic.DetailView):
model = Idea
template_name = "idea/detail.html"
def get_context_data(self, **kwargs):
context = super(IdeaDetailView, self).get_context_data(**kwargs)
context['results'] = Idea.objects.filter(idea_id=self.kwargs.get('pk'))
return context
Admin.py:
class IdeaAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("idea_title",)}
I want to add a slug which is the idea_title, however if I try to add that into the URL it fails.
Try adding a slugfield, get_absolute_url method, and save method to your Idea model, like this:
from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
class Idea(models.Model):
idea_id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Searches ID')
idea_number = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
idea_title = models.CharField(max_length=300, blank=True, null=True)
idea_text = NonStrippingTextField(max_length=10000, blank=True, null=True)
slug = models.SlugField(
default='',
editable=False,
max_length=300,
)
def get_absolute_url(self):
kwargs = {
'pk': self.id,
'slug': self.slug
}
return reverse('idea-pk-slug-detail', kwargs=kwargs)
def save(self, *args, **kwargs):
value = self.title
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
Then, in urls.py:
path('idea/<int:pk>-<str:slug>/', views.IdeaDetailView.as_view(), name='detail')
And, in views.py right under template_name:
query_pk_and_slug = True
Two more methods and more info found HERE.
Hope this helps!

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance

Overriding Django profiles' profile_detail view

I installed django profiles/registration and everything seems to be fine. When a user registers their profile is created also. Now what i want to do is query another Model which is Company based on the user id of User. I dont want to change django-profiles view but add the extra field on urls to match and query Company model. When i hardcode the url (ex:put the id number of the userprofile like so userprofile=1, it works.). So when a user is logged in and goes to profile detail page Company assigned to them is queried based on their user.id.
class UserProfile(models.Model):
user = models.OneToOneField(User)
#email = models.CharField(max_length=200, blank=True, null=True)
# Other fields here
#company = models.ForeignKey(Company,blank=True,null=True)
#office = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.user.username
class Company(models.Model):
userprofile = models.ForeignKey(UserProfile, null=True, blank=True)
comp_name = models.CharField(max_length=200,blank=True,null=True)
comp_address = models.CharField(max_length=200,blank=True, null=True)
comp_email = models.CharField(max_length=200,blank=True, null=True)
comp_zip = models.IntegerField(blank=True, null=True)
comp_phone = models.IntegerField(blank=True, null=True)
comp_city = models.CharField(max_length=200,blank=True, null=True)
#comp_state = models.USStateField(blank=True, null=True
comp_state = models.CharField(blank=True, max_length=2)
compwebsite = models.URLField(max_length=200, blank=True, null=True)
twitterurl = models.URLField(max_length=200, blank=True, null=True)
facebookurl = models.URLField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.comp_name
url(r'^profiles/(?P<username>\w+)/$', 'profiles.views.profile_detail', {'extra_context':{'queryset':Company.objects.filter(userprofile=request.user.id)}},),
You might want to call it from inside a view
from *** import profile_detail
def my_view(request, username):
extra_context = {}
return profile_detail(request, queryset=Company.objects.filter(userprofile=request.user.id),
template_name="my_template.html",
paginate_by=20,
extra_context=extra_context)